bitemporal-runtime 0.1.0

Bitemporal truth primitives — valid_time/recorded_time tracking, append-supersede, as-of queries, temporal snapshots.
Documentation
# bitemporal-runtime

Bitemporal truth primitives for append-supersede temporal data.

`bitemporal-runtime` is the only Rust implementation of strict
bitemporal truth — separate `valid_time` and `recorded_time`,
append-supersede, as-of queries. As of release, **zero analogues
exist on crates.io**.

## When to use it

Bitemporal data modeling is for domains where you need to answer
"what did we believe at time T about the fact that was valid at
time V" — audit, regulatory reporting, scientific reproducibility,
financial historical reconstruction, version-of-truth queries.

If you have one timeline (just `updated_at` or just `valid_from`),
you don't need this crate. If you have two, and you need both, you
do.

## Quick Start

```rust
use bitemporal_runtime::{append_supersede, as_of_query, BitemporalRecord};
use chrono::{TimeZone, Utc};

fn main() {
    // Build a fact at two points in time — first we believed one
    // thing, then a corrected version came in.
    let t0 = Utc.timestamp_opt(1000, 0).unwrap();
    let t1 = Utc.timestamp_opt(2000, 0).unwrap();
    let t2 = Utc.timestamp_opt(3000, 0).unwrap();

    let mut records: Vec<BitemporalRecord<&str>> = Vec::new();
    append_supersede(
        &mut records,
        BitemporalRecord {
            id: "us-presidents".to_string(),
            valid_time: t0,
            recorded_time: t0,
            value: "Roosevelt",
        },
    ).unwrap();
    let receipts = append_supersede(
        &mut records,
        BitemporalRecord {
            id: "us-presidents".to_string(),
            valid_time: t0,
            recorded_time: t1,
            value: "Truman",
        },
    ).unwrap();

    // Supersession receipts are the audit handle for the change.
    // SHA-256 digests bind every record field (id, temporal, value).
    assert_eq!(receipts.len(), 1);
    assert_eq!(receipts[0].superseded.superseded_id, "us-presidents");
    assert_eq!(receipts[0].superseding_id, "us-presidents");

    // "As of T=1500, what did we believe was the value of
    // `us-presidents` at T=2000?" — answer: Truman, because that
    // was the latest recorded version by 1500.
    let as_of = as_of_query(&records, t1, Utc.timestamp_opt(1500, 0).unwrap());
    assert_eq!(as_of[0].value, "Roosevelt"); // only v1 was known by T=1500

    // "As of T=2500, what did we believe was valid at T=3000?" —
    // answer: the latest known version, v2 (Truman).
    let as_of = as_of_query(&records, t2, Utc.timestamp_opt(2500, 0).unwrap());
    assert_eq!(as_of[0].value, "Truman");
}
```

## Overview

This crate provides first-class support for bitemporal data modeling:

- **valid_time**: When a fact is true in the business domain
- **recorded_time**: When the system captured the fact
- **append-supersede**: Updates append new rows instead of mutating existing ones

## Key types

- `BitemporalRecord<T>` — a temporal record with `valid_time`, `recorded_time`, and domain value
- `SupersessionReceipt` — cryptographic SHA-256 receipt for supersession events
- `InMemoryDb` — in-memory store for testing
- `SqliteDb` (feature `sqlite`) — durable SQLite-backed store

## Core functions

- `append_supersede()` — append a new record, emit receipts for superseded prior versions
- `as_of_query()` — query records valid at a given `valid_time` as of a given `recorded_time`
- `temporal_snapshot()` — retrieve full state as of a given `recorded_time`

## Cargo features

- `sqlite` — enables the `SqliteDb` durable store (adds `rusqlite` dep)
- `schema` — enables JSON Schema generation for the public types (adds `schemars` dep)

Both features are off by default to keep the dep graph small.

## MSRV

Rust 1.75 (2021 edition).

## Dependencies

Default: `chrono`, `serde`, `serde_json`, `sha2`, `thiserror`.
With `sqlite`: adds `rusqlite`.
With `schema`: adds `schemars`.

## License

MIT OR Apache-2.0 (dual-licensed). See `LICENSE-MIT` and
`LICENSE-APACHE` for the full texts.

## Changelog

See `CHANGELOG.md` for the release history.