NEDB Docs / Core concepts / Provenance & Time

Every database stores what. NEDB also stores when, when it was true, and why — as queryable, indexed primitives, sealed in the same hash chain as the data itself.

Two time axes

Transaction timeAS OF <seq> — answers "what did the system know at sequence N?" The engine never garbage-collects versions, so every past state is reachable forever (unless an operator explicitly runs compact(), which announces that it is trading history for space).

Valid timeVALID AS OF "<date>" — answers "what was true in the world on that date?" A row can carry valid_from/valid_to independent of when it was written, which is bi-temporality: the two axes compose.

# What did the system know at seq 200 about what was true on 2024-02-15?
db.query('FROM policy AS OF 200 VALID AS OF "2024-02-15"')

Causal provenance

A write may carry caused_by — a list of parent writes — plus evidence and confidence. This is not a comment field: it is an indexed edge in the DAG. TRACE caused_by walks ancestors (why did this happen?); TRACE caused_by REVERSE walks descendants (what did this cause?).

db.put("inputs", "msg_1", {"text": "user prefers dark mode"})
seq_msg = db.seq
db.put("beliefs", "dark_mode", {"value": True},
       caused_by=[seq_msg], evidence="user_message", confidence=0.95)

db.query('FROM beliefs WHERE _id = "dark_mode" TRACE caused_by')      # → msg_1
db.query('FROM inputs  WHERE _id = "msg_1" TRACE caused_by REVERSE')  # → dark_mode
Call-shape fact (learned the hard way): caused_by goes at the top level of a put request, not nested inside the document. A caused_by inside the document is stored as ordinary user data and creates no edge. Stored rows expose the edge as _caused_by.

The three commitments

CommitmentAnswersWhere
Merkle head (head)"did this database's history change?" — chains every write by seq and object hashreturned on every response
State root"do two databases say the same thing right now?" — computable from live state alone, equal for equal state regardless of routedocs/state-root-v1.md, test vectors in vectors/
Merkle proofs"prove this row existed at this time" — verifiable locally, without the serverproof() / verify_proof()

Keeping head and state root separate is deliberate: a root that folded in history could not compare two replicas that arrived by different routes — and that comparison (replica agreement, drift detection, anchoring) is most of what a root is for. One subtlety the spec pins: with encryption on, a node's hash is a function of its ciphertext (a fresh AES-GCM nonce per write), so state-root leaves commit to logical content, never object hashes.

Provenance in SQL

The metadata is selectable like any column, and settable from SQL — the causal chain does not require the HTTP API or the Python client:

SELECT _id, _hash, _seq FROM audit ORDER BY _seq;
INSERT INTO audit (_id, _caused_by, kind) VALUES ('leaf', '<parent-hash>', 'reprice');
SELECT _id FROM audit TRACE caused_by;

What tamper-evidence actually means

verify() recomputes the chain (~21,000 BLAKE2b/sec; 30k objects in 1.38 s in the dated benchmark). Content tampering is never masked — the self-healing pass repairs structural gaps only, and a tampered log verifies false. A durable store answers "not available at that sequence" for pruned history rather than returning a stale value.