Every fact in Substrate exists in two time dimensions. Understanding this model is essential for using the system correctly.
Valid time represents when a fact was actually true in the real world. If Alice became the owner of the payments service on January 15th, that's the valid-time start.
System time represents when the fact entered the system. If the ownership change was recorded on January 18th, that's the system-time. The 3-day gap is normal - systems always learn about reality with delay.
With two time dimensions, you can answer questions that are impossible with a single timestamp:
Query at valid-time = Dec 15, system-time = now. Returns what we currently know about reality at that point. Uses latest knowledge.
AS-OF VALID Dec-15 AT SYSTEM HEAD() Query at system-time = Dec 15. Returns what the system knew at that point - even if we later learned it was wrong.
AT SYSTEM Dec-15 Diff query on system-time. Shows all facts that were added, corrected, or retracted - regardless of their valid-time.
BETWEEN SYSTEM Jan-8 AND Jan-15 Compare system-time = then vs system-time = now for the same valid-time. Reveals corrections and updated understanding.
DIFF AT SYSTEM Jan-1 vs HEAD() FOR VALID Dec-15 Every versioned fact in Substrate carries both time dimensions:
bitemporal_version:
subject → entity_id or edge_id
field → property key or relation predicate
value → the actual data
valid_from → when this became true in reality
valid_to → when this stopped being true (∞ if still true)
sys_from → when the system recorded this
sys_to → when this was superseded (∞ if current)
assertion_id → provenance reference
integrity → hash for verification [from, to)valid_to = +∞ means "still true"sys_to = +∞ means "current version"All operations are append-only. Nothing is ever mutated or deleted from the truthlog.
Add a new fact about reality
Creates a new bitemporal version with sys_from = now,
sys_to = ∞. The valid interval is specified by the caller.
INSERT: Alice owns payments
valid_from: 2025-12-01
valid_to: ∞
sys_from: 2025-12-05 (when we learned)
sys_to: ∞ We learned an earlier record was wrong
Closes the old version's system interval and inserts a new version with the corrected data. History is preserved - you can still see what we believed before.
CORRECT: Actually Bob owns payments
Old version:
sys_to: 2025-12-10 (closed)
New version:
valid_from: 2025-12-01
valid_to: ∞
sys_from: 2025-12-10 (correction time)
sys_to: ∞ Late-arriving data about the past
Same as insert, but valid-time is in the past. Common when ingesting historical data or receiving delayed updates.
BACKFILL: Found old incident from August
valid_from: 2025-08-15
valid_to: 2025-08-16
sys_from: 2025-12-10 (when ingested)
sys_to: ∞ We no longer assert this claim
Closes the system interval and may insert a "unknown" sentinel. The retraction event is itself recorded for audit.
RETRACT: We're not sure who owns payments anymore
Old version:
sys_to: 2025-12-15 (retracted)
Retraction event recorded with reason Entity resolution - two records are the same thing
Creates a same_as link between entities. Neither
entity is deleted. Query layer can canonicalize via resolution rules.
MERGE: service-123 and svc-123 are the same
Creates: same_as(service-123, svc-123)
Both IDs remain valid, queries can resolve
The AS-OF clause specifies which versions to return:
AS-OF(valid=t, system=s) returns versions where:
valid_from <= t < valid_to
AND
sys_from <= s < sys_to valid=now, system=now - What's true right now according
to our current knowledge.
valid=past, system=now - What was true then, with
everything we know now (including corrections).
valid=now, system=past - What we believed was true
at that point (might include errors we later corrected).
valid=past, system=past - What we believed at time S
about what was true at time V.
These properties are guaranteed and enforced by the system:
I1 Once a commit is written, it is immutable. System intervals only close, never reopen. Corrections append new versions.
I2 For a given lineage (subject + field), system intervals do not overlap. At any system-time, exactly one version is "current".
I3 Commit IDs strictly increase within a tenant. Ordering is total and deterministic.
I4 Every version's integrity hash can be verified against its canonical encoding. Hash chain connects commits.
I5 Every version has exactly one assertion_id linking to its provenance record.