Engrams
Every mutation in patinaDB is recorded. A engram is one committed unit of change — a list of low-level delta operations (create/delete vertex, set property, set/remove label, create/delete edge) plus metadata: an id, a parent id, a timestamp, and an optional message. The chain of engrams is the source of truth for history, diffs, time travel, and — in the server — replication.
Think of it as a git-like commit log for your graph: an append-only history you can inspect, diff, travel through, tag, and compact.
How writes become engrams
Autocommit (the common case). Every write — embedded Dataset::query, a REST
/cypher call, or a Bolt RUN — is captured and committed as one engram,
atomically. A single statement, however complex (MATCH … CREATE … SET …, or a
bulk UNWIND … CREATE), is one engram.
#![allow(unused)]
fn main() {
let ds = Dataset::open("./mygraph")?;
ds.query("CREATE (n:Person {name: 'Ada'})", None)?; // one engram
ds.query("CREATE (m:Person {name: 'Charles'})", None)?; // another engram
}
Explicit transactions. Over Bolt, BEGIN … COMMIT groups several statements
into one engram applied atomically at COMMIT (see
Bolt). ROLLBACK discards them — no engram is written.
Embedded staging. The library also exposes manual staging: begin opens an
in-memory pending engram, you stage ops, and commit applies them as one
engram.
#![allow(unused)]
fn main() {
let pending = ds.begin(Some("seed".into()));
// … stage ops into `pending` …
let meta = ds.commit(pending)?; // one engram, applied atomically
}
Listing history
CALL patinadb.engrams() YIELD id, message, timestamp
RETURN id, message, timestamp ORDER BY timestamp DESC
patinadb ./mygraph log # CLI
The MCP server exposes the same as the engrams tool.
Snapshots & compaction
To keep time travel fast, patinaDB periodically captures a full-graph snapshot (by default every 50 commits, configurable). Reconstructing a past state loads the nearest snapshot and replays deltas forward from there, rather than replaying the whole history. Snapshots are an internal optimisation — you interact with history through engrams, diffs, tags, and time travel.
Determinism & replication
A engram is a pure, deterministic description of a change: replaying a committed op stream reproduces the exact same graph, and a created vertex’s generated UUID is baked into the op so replays are stable. This is what lets the server replicate — a write becomes a Raft log entry of delta ops, and every node applies the same ops to reach the same state.
The engram lifecycle
History is a managed asset, not just an append-only ledger. These operations let you name, protect, compact, branch, and promote points in it.
Pin — protect a engram from compaction
A pinned engram is never coalesced by squash, so the point-in-time it marks stays reachable.
CALL patinadb.pin('<engram-id>')
CALL patinadb.unpin('<engram-id>')
Tag — a named, snapshotted, pinned point
A tag is a stable name for a engram (like a git tag), so you can refer to a meaningful point without tracking raw ids. Creating a tag pins its engram and takes a full snapshot there, so reading it back is cheap and squash never removes it.
CREATE TAG v1 -- tag the current HEAD
CREATE TAG release AS OF '<engram-id>' -- tag a specific engram
SHOW TAGS -- list name → engram
DROP TAG v1 -- remove (unpins if unreferenced)
Read a database as it was at a tag with
AS OF TAG. Tags
replicate across a cluster — every node names, pins, and snapshots the same
engram — so SHOW TAGS and AS OF TAG work against any node.
Squash — compact old history
Coalesce a run of old engrams into a single synthetic genesis, keeping recent history intact. Pinned (and too-recent) engrams are boundaries squash stops at. The live graph is unchanged; only the log is compacted.
CALL patinadb.squash(10) -- keep the 10 most recent, coalesce older
CALL patinadb.squash(10, 1719792000) -- …only those older than a unix timestamp
Fork — branch a database at a point
Create a new database seeded with the state of another at a chosen engram (HEAD if omitted). The fork starts with a single genesis engram and its own independent history.
FORK DATABASE prod AS OF '<engram-id>' INTO staging
Restore — promote a past state to HEAD
Bring the state at a past engram back to the live graph as a new engram. History is preserved (nothing is rewritten); the restore is an ordinary append-only write, so it replicates cleanly.
CALL patinadb.restore('<engram-id>')
This is the write-side counterpart to time travel (which is read-only): time travel reads the past, restore promotes it to the present.
Subscribing to changes (CDC)
The engram log is also a live change source: the server’s
GET /changes endpoint streams each committed engram as
it applies, resumable from an engram cursor — so external systems can react to
graph changes as they happen (cache invalidation, search-index sync, ETL).
In the cluster
All of the lifecycle operations are replicated control commands: every node
re-derives the result from its own identical history (synthetic genesis ids are
content-derived, so the ids agree on every node). SHOW TAGS / SHOW DATABASES
are local reads; the mutating commands go through the leader and need admin.