Tiramemsu

How tiramemsu differs from oxilite

Tiramemsu articles · 30 September 2026

oxilite is a shipped, Oxigraph-compatible RDF database on SQLite, by the same author. Tiramemsu started from what oxilite taught and asks a different question. oxilite asks “how do I run standard RDF and SPARQL anywhere SQLite runs, including Cloudflare D1?”. Tiramemsu asks “what should an agent's memory look like if every fact can be corrected, doubted and revisited?”. They are complements more than competitors, and it is honest to start with the difference in maturity.

Maturity first

oxilitetiramemsu
StatusShipped, published, documented, with bindings and a studioNew. Designed and implemented in September 2026, one library, with Node.js and Python packages that are built but not yet published
ConformanceOxigraph's own W3C and store tests run on it; 96 % of the openCypher TCK634 of 781 in-scope W3C tests; 67 % of the TCK (deviations listed, not hidden)
Where it runsrusqlite, dlopen, Turso, Cloudflare D1, WebAssembly, Durable Objectsrusqlite only (an executor trait leaves room for more)
Also hasDatalog, OWL reasoning, SHACL and ShEx, JSON-LD and credentials, full-text, vectors, Python and Node bindings, MCPNone of these yet, except Node.js and Python packages that are built but not published. Full-text and vectors are planned

If you need SPARQL on D1, reasoning, or a drop-in for Oxigraph today, use oxilite. What follows is where the designs differ, not a claim that tiramemsu does more.

1. What is a statement?

This is the root difference. In oxilite a statement is a quad (s, p, o, g): a key in a clustered table, so the same triple in the same graph is one row, as RDF says. To say something about a triple, oxilite uses RDF 1.2 reifiers: a separate node that points at a “triple term”. It adds a reifier only when needed, for relationship properties or parallel edges.

In tiramemsu a statement is a row with its own id, (eid, s, p, o), plus a lifetime. Two rows with the same (s, p, o) are two occurrences, each with its own history. The id can be the subject or object of any other statement, which is what makes layered graphs a native feature and not an encoding.

Concernoxilite (quads + reifiers)tiramemsu (statement ids)
Annotate a factTriple term + reifier node + the annotation: 3 rows1 row whose subject is the fact's id
Annotate an annotationAnother triple term and reifier1 row again
Same fact, two episodesOne row; history is in a separate change logTwo rows, each with lifetime and valid time
Retract with its annotationsAmbiguous: reifiers are shared by every occurrenceCascade over subject and object positions
RDF semanticsSet of quads, standardSet semantics recovered per query, bag for Cypher
GraphsA column in every keyA node, plus a membership statement

2. How is time stored?

oxilite keeps quads as the present and records changes beside it in an append-only quad_log, written by triggers, at an opt-in versioning level. A past state is read from the log with a probe per pattern. That keeps present-day queries exactly as fast as before, and it is a good fit for D1, where every index entry is billed.

Tiramemsu makes time part of the row: t_add and t_ret for when the database believed it, and v_from and v_to for when it was true. It is always on and bitemporal, which oxilite is not (it has no valid time). Current state is served by partial indexes on t_ret IS NULL, and the past by full covering indexes with the newest version first. The bet is that a past read is one index range, not a log probe. That bet is not yet tested head to head; oxilite's own benchmark harness is queued to run on both.

Tiramemsu also refuses to forget. SQLite triggers reject DELETE and any second change to a row, inside the file. oxilite has a real purge that rewrites history. Tiramemsu plans crypto-shredding for legal erasure instead: destroy a key, keep the rows.

3. What can you do to a fact?

oxilite gives you SPARQL Update: insert and delete triples, with commits, diffs and time travel around them. Tiramemsu adds memory verbs, because a memory needs more than insert and delete:

4. Two languages, one IR

oxilite lowers Cypher to SPARQL algebra and evaluates what SQL cannot in a Rust tail. That reuses its planner and works well: it passes 96 % of the TCK. Tiramemsu has one logical IR with explicit semantic flags (set or bag, homomorphism or relationship isomorphism, unbound or three-valued null), so both languages sit beside each other, and a differential suite checks that equivalent queries agree. The cost is that tiramemsu's Cypher runs part of its work in an interpreter, and its TCK score is lower for now.

Tiramemsu also has a native path engine, an automaton search that batches neighbour lookups per layer. It exposes a tm_path table function and can cross layers. oxilite uses recursive CTEs seeded from a bound endpoint.

5. Where it runs

oxilite's core does no I/O. It emits SQL requests and consumes responses, so the same compiler runs against rusqlite, D1 or JavaScript. Every write is one atomic batch, because D1 offers nothing else. Tiramemsu's engine reads before it writes (idempotent assert, cascades, unique checks), so it needs interactive transactions and cannot run on D1. It talks to SQLite through a small synchronous trait, which leaves room for Durable Objects, Turso or WebAssembly, and for now only rusqlite is implemented.

6. A benchmark: what does an id cost?

To check the cost of statement ids, the same 750 000 triples were loaded into two layouts on SQLite, with 10 % of the knows edges carrying a confidence and 1 % of those carrying a source. A is tiramemsu's layout (id as rowid, annotations as rows about the id). B is oxilite's model (clustered (s,p,o) key, triple terms and reifiers). Both had three covering permutations and live data only.

MeasureA: idsB: reifiersB vs A
File size53.6 MB35.5 MBB is 34 % smaller
Plain point lookup2.7 µs2.8 µssame
A node's edges with their confidence6.5 µs8.7 µsA is 1.3× faster
All edges with confidence ≥ 952.1 ms5.2 msA is 2.5× faster
Source of a confidence (2 levels)5.0 µs5.3 µsabout equal
2-hop join, SPARQL set semantics10.3 µs4.9 µsB is 2× faster
2-hop join, bag semantics5.0 µs4.7 µssame
Write an edge and a confidence35 µs47 µsA is 1.3× faster

The id costs about half again in storage and pays for itself on annotation-heavy reads and writes. It loses on set-semantics joins, where duplicates must be removed, and tiramemsu now skips that step for predicates that have never held two ids for the same triple (halving that join in the same test). The script is in the repository under bench/eid-vs-reifier/. It is one machine and a synthetic data set, so treat it as a direction, not a verdict.

oxilite reports its own numbers for history: about 4.8 rows written per triple without versioning and 8.8 with its full as-of index, and past reads about 3× the present for subject-bound patterns, 10–100× without the as-of index for predicate-bound ones. Tiramemsu's full schema stores about 150 bytes per statement, and the as-of lookup slows down as one key collects many updates (2.7 µs at one update per key, 45 µs at a thousand). Both have a weak spot in history. They are different ones.

Which should you use?

You wantUse
Standard RDF and SPARQL, Oxigraph compatibility, D1, reasoning or SHACLoxilite
Facts that carry provenance, confidence and beliefs, with corrections and exact history of both belief and truthtiramemsu
A mature dependency todayoxilite

Tiramemsu borrowed from oxilite on purpose: statistics as a precondition for SQLite join ordering, allow-list test harnesses where every expected deviation has a reason, SERVICE rather than GRAPH for scoping time, and the retrieval design planned next.

← Layered graphs, explained