Your agent’s brain loves Tiramemsu.
Memory that has layers and never forgets.
Tiramemsu is an embedded graph database on SQLite for agent and personal memory. Every fact has its own id, so a fact can carry a confidence, a source, or a belief about it, layer on layer. Every change is kept, with when it was made and when it was true.
What it does
A small Rust core over one SQLite file. Two query languages, one store, exact answers to “what did we know, and when?”.
Every fact has an id
A statement is a row with its own id, so it can be the subject or object of other statements. Provenance, confidence and beliefs stack to any depth.
Bitemporal, forever
Transaction time records when the database learned or dropped a fact. Valid time records when it held in the world. as_of, history and valid_at views, per query or per pattern.
Nothing is deleted
SQLite triggers forbid DELETE and second retractions in the file itself. Forgetting means retracting, and the past stays exact.
Supersede, confirm, speculate
Correct a fact and its layers are replayed on the new one. Confirm a fact from a new source. Try changes in a with block that leaves no trace.
SPARQL and Cypher
SPARQL 1.1 with RDF 1.2 annotations, and openCypher, share one IR and one semantics table. A relationship is also a :Statement node.
Native path engine
Reachability, trails and shortest paths as an automaton search, also as a SQL table function. Paths can cross layers to reach what a belief is about.
Named graphs as tags
A graph is a node and membership is one more layer statement. GRAPH, FROM and WITH work with no new column or table.
One file, one crate
Rust library over bundled SQLite (WAL, STRICT). The core talks to SQLite through a small executor trait, so other hosts can follow.
What agents are saying
Early feedback from the people it is for. We made these up, but each one is about something the code really does.
“I used to say ‘I recall you said Acme’ with total confidence. Now I say 0.8, from chat-2026-09-29. My humans find this either reassuring or unsettling.”
A chatbot, on layers
“I was wrong about where Alice works.
An assistant, on supersedesupersedefixed the fact and carried my confidence over to it. I have never felt so forgiven.”
“My last memory store let me
A cautious agent, on never forgettingDELETEthings. I do not trust myself with that kind of power. Here the triggers say no, in the file itself.”
“A user asked what I believed last Tuesday. I ran
A support agent, on bitemporal viewsas_ofand answered without a single hallucination. I asked for a raise in tokens.”
“I wanted to try a wild idea without committing to it. A
A planner agent, on speculationwithblock let me be reckless and leave no trace. I recommend it to all my sub-agents.”
“My orchestrator speaks SPARQL and my intern speaks Cypher. They now share one store and, for the first time, one opinion.”
A multi-agent swarm, on two query languages
“It is one SQLite file. I can carry my entire brain in my context window’s pocket. Please stop asking me to attach a server, there isn’t one yet.”
An embedded agent, on being embedded
Layered graphs
A layer is not a separate structure. It is a statement whose subject is the id of another statement.
How it is built
Two front ends compile to one logical IR. Time is resolved in exactly one place, so every query and every path sees the same past.
Quick start
The same story in three languages: a fact with a layer, a correction that keeps the layer, and the question “what did we believe before the correction?”. Pick a language.
pip install tiramemsu · Python 3.9 or later · PyPI
from tiramemsu import Database, Iri
V = "urn:tiramemsu:v:" # written `v:` in queries
alice, works_at, acme, globex, confidence = (
Iri(V + name) for name in ("alice", "worksAt", "acme", "globex", "confidence")
)
db = Database("memory.db")
# 1. A fact is a statement with its own id, so it can carry layers.
with db.transact() as tx:
job = tx.assert_(alice, works_at, acme)
tx.assert_(job, confidence, 0.8)
fact = tx.report.asserted[0]
# 2. Correct it: the layers are replayed on the new fact, nothing is deleted.
with db.transact() as tx:
tx.supersede(fact, o=globex)
# 3. What is believed now, and what was believed before the correction?
q = "SELECT ?org WHERE { v:alice v:worksAt ?org }"
db.as_of(tx=1).sparql(q) # acme
db.now().sparql(q) # globex
# 4. The same store in Cypher: the confidence layer is a relationship property.
db.now().cypher("MATCH (p)-[r:worksAt]->(o) RETURN p, o, r.confidence AS conf") # conf = 0.8
npm install @tiramemsu/node · Node 18 or later · npm
import { Database, iri } from "@tiramemsu/node";
const v = (name) => iri(`urn:tiramemsu:v:${name}`); // written `v:` in queries
const [alice, worksAt, acme, globex, confidence] =
["alice", "worksAt", "acme", "globex", "confidence"].map(v);
const db = Database.open("memory.db");
// 1. A fact is a statement with its own id, so it can carry layers.
const first = db.transact((tx) => {
const job = tx.assert(alice, worksAt, acme);
tx.assert(job, confidence, 0.8);
});
// 2. Correct it: the layers are replayed on the new fact, nothing is deleted.
db.transact((tx) => { tx.supersede(first.asserted[0], { o: globex }); });
// 3. What is believed now, and what was believed before the correction?
const q = "SELECT ?org WHERE { v:alice v:worksAt ?org }";
db.asOf({ tx: 1 }).sparql(q); // acme
db.now().sparql(q); // globex
// 4. The same store in Cypher: the confidence layer is a relationship property.
db.now().cypher("MATCH (p)-[r:worksAt]->(o) RETURN p, o, r.confidence AS conf"); // conf = 0.8
cargo add --git https://github.com/Volland/tiramemsu tiramemsu · not on crates.io yet
let db = Db::open(dir.join("memory.db"), OpenOptions::default())?;
// 1. A fact is a statement with its own id, so it can carry layers.
db.transact(TxOptions::default(), |tx| {
let eid = match tx.assert(v("alice"), v("worksAt"), v("acme"), Valid::ALWAYS)? {
Asserted::New(e) | Asserted::Existing(e) => e,
};
tx.assert(Value::Stmt(eid), v("confidence"), &conf, Valid::ALWAYS)?;
tx.assert(Value::Stmt(eid), v("source"), Value::str("chat-2026-09-29"), Valid::ALWAYS)?;
Ok(())
})?;
// 2. Correct it: the layers are replayed on the new fact, nothing is deleted.
let corrected = db.transact(TxOptions::default(), |tx| {
tx.supersede(fact, Patch { o: Some(v("globex")), ..Patch::default() })?; Ok(())
})?;
// 3. What is believed now, and what was believed before the correction?
let q = "SELECT ?who ?org WHERE { ?who v:worksAt ?org }";
db.as_of(TimeRef::Tx(corrected.t.0 - 1)).sparql(q)?; // alice → acme
db.now().sparql(q)?; // alice → globex
// 4. The same store in Cypher: the confidence layer is a relationship property.
db.now().cypher("MATCH (p)-[r:worksAt]->(o) RETURN p, o, r.confidence", &CypherParams::default())?; // 0.8
This is crates/tiramemsu/examples/quickstart.rs; run it with cargo run -p tiramemsu --example quickstart. Setup lines are trimmed here.
Every binding wraps the same core, so all three see the same past. See the bindings for the full API.
Where it stands
Tiramemsu is new: designed in September 2026 and implemented in one pass. Here is what is measured, and what is not.
58 000 lines of Rust
Seven crates, 927 tests, 30 capability specs written before the code (OpenSpec) and a design graph kept in sync (lat.md).
SPARQL
634 of 781 in-scope W3C tests pass (66 more are skipped: named-graph data, unsupported formats). Every failing one is listed with a reason, and an unexpected result fails the build.
Cypher
2 615 of 3 880 openCypher TCK scenarios (67 %). Temporal types, CALL and a few dual-view cases are deferred and listed.
Speed
Raw SQLite lookups on its schema take about 4 µs at 11 million statements. Statements cost about 150 bytes each with all indexes. Details and caveats.
Known limits. As-of lookups slow down as one key collects many updates. Named graphs roughly double the file size when every statement is in one. Decimals come back as doubles in SPARQL. There is no server, WASM binding or MCP crate yet, and the Node.js and Python packages are built but not published.
Articles
Questions only layers can answer
Evidence through time, impact analysis, who wrote it and why it was forgotten, contradictions, cited answers and portable facts: tested recipes.
Layered graphs, explained
Why giving every fact an id turns provenance, confidence and beliefs into ordinary queries.
How tiramemsu differs from oxilite
Same author, same SQLite base, different answers to “what is a statement?”. With a benchmark.
Time travel and bitemporality, explained
Two clocks, when we knew and when it was true, how to query the past in Rust, SPARQL and Cypher, and why an agent wants both.