Bitemporal

What did the system know?

Every fact in InvariantDB carries two clocks. One tracks when it was true in the world. The other tracks when the system learned it. They move independently — because reality and record-keeping move independently.

That's why the same query, at different clocks, returns four different answers — and every one of them is correct. What did we believe at the time? What do we believe now? What was true then? What is true now? One line of Cypher, four provable answers, one audit chain that doesn't move.

See it live → Cypher reference 30-second interactive demo · no signup

“What did the system know at that time?” Drag the two sliders. The top one moves recorded time — when the system learned the fact. The bottom one moves valid time — when the fact was true in the world. Watch the same Cypher query return different answers at different clocks. The audit chain doesn't move.

The same query. belief-99 was revised on 2026-06-01 — from HIGH_RISK to LOW_RISK after KYC completed. Ask what the system knew before the revision → HIGH_RISK 0.70. Ask what it knew afterLOW_RISK 0.95. Both are true. Both are provable. The chain never rewrites.

The two clocks

Reality and record-keeping don't move together.

Most databases track one clock — now. That's fine for a shopping cart. It falls apart the moment someone asks “why did we act on this?” six months later. InvariantDB tracks both clocks natively, on every fact.

Clock 1 · Valid time

When it was true in the world.

The customer's KYC status became verified on 2026-06-01. That's a fact about the world — it happened whether or not any system captured it in the same instant.

Valid time answers “what was true then?” You can backdate it, forward-date it, and slice a fact's validity window without touching the audit trail.

// Customer was verified in the world on: 2026-06-01T09:15:00Z
Clock 2 · Recorded time

When the system learned it.

The KYC document didn't arrive at the risk service until 2026-06-03 at 14:22 UTC — two days later. That's a fact about our record-keeping, not the world.

Recorded time answers “what did we know?” It's monotonic, append-only, and hash-chained. You cannot rewrite what the system knew — you can only append a new revision.

// The system first learned it on: 2026-06-03T14:22:41Z

Same query, four different answers — all correct.

Pin recorded time and slide valid time. Pin valid time and slide recorded time. Do both. That's the four quadrants of bitemporal — and it's what separates an audit-grade store from a snapshot-and-hope-for-the-best one.

 
VALID = March 11
VALID = today
RECORDED
= March 5
HIGH_RISK · conf 0.70
what we believed then, about then
HIGH_RISK · conf 0.70
what we believed then, about today
RECORDED
= today
HIGH_RISK · conf 0.70
what we believe now, about then
LOW_RISK · conf 0.95
what we believe now, about today
Cypher syntax

AT VALID and AT RECORDED

Two clauses, one Cypher pattern each. Attach either or both to a MATCH pattern. The engine rewinds the store to that clock before pattern matching begins.

Pick a recorded time. Ask what the system knew.

// belief-99 as the system knew it on March 5. MATCH (b:Belief {id: "belief-99"}) AT RECORDED "2026-03-05T00:00:00Z" RETURN b.claim, b.confidence;

Filter after the rewind. Recall memory the way an agent saw it.

// Salient memories as of June 1, filtered on that day's salience. MATCH (m:Memory) AT RECORDED "2026-06-01T00:00:00Z" WHERE m.salience > 0.5 RETURN m;

Combine both clocks. This is the four-answers query.

// What did we know on March 5, about the world as it was on March 11? MATCH (b:Belief {id: "belief-99"}) AT RECORDED "2026-03-05" AT VALID "2026-03-11" RETURN b.claim, b.confidence;
Where they attach. AT VALID and AT RECORDED bind to a MATCH pattern — they immediately follow the pattern and rewind the store before the pattern is executed. They do not attach to WITH, WHERE, or RETURN. Put filters and projections after the clock clauses.
Anatomy of a decision

A decision was made on March 5. On June 1 someone asks “why?”

The scenario every regulated team lives with. Auditor walks in. Points at a decline. Wants to know: why did the system decide that? On March 5 the risk service returned HIGH_RISK. On June 1, after KYC, the same customer is LOW_RISK. Both facts live in InvariantDB. Neither one erases the other.

What the system knew at the time.

Ask the store to rewind to March 5. The KYC document hadn't arrived yet. belief-99 was on revision 1, backed by two source episodes. The service returned HIGH_RISK with 0.70 confidence — and the decline queue kicked in.

MATCH (b:Belief {id: "belief-99"}) AT RECORDED "2026-03-05T00:00:00Z" RETURN b.claim, b.confidence;
→ claim = HIGH_RISK · confidence = 0.70 · recorded rev 1

What the system knows today.

Ask the store with no clock clause. It returns the latest revision — the one recorded after KYC verified the customer on June 1. Confidence is 0.95. The old revision is still there, still queryable, still hash-chained. Nothing was overwritten.

MATCH (b:Belief {id: "belief-99"}) RETURN b.claim, b.confidence;
→ claim = LOW_RISK · confidence = 0.95 · recorded rev 2
Both queries are true. Both are provable. The March 5 answer explains the decline. The today answer explains the current customer state. The audit chain covers every recorded revision between them. Your auditor asks “why?” — you show the March 5 rewind. Your fraud team asks “should we still block?” — you show today. One database. One Cypher grammar. Two clocks, both queryable.

Scrub a real timeline.

The interactive demo. Drag the March 5 scrubber, watch the same Cypher line reconstruct what the system knew at that instant. No signup.