Skip to content

Architecture

dent8 is a memory integrity platform, not a memory provider. The platform owns correctness around memory writes and reads: provenance, freshness, contradiction handling, supersession, replay, authority, and explainability.

The first product boundary is one runtime with three surfaces:

  • Memory firewall: validates fact-event writes before they alter projections.
  • Versioned memory store: persists immutable events and materialized current state.
  • Memory debugger: replays event streams and explains why state looks the way it does.

These surfaces share one event model. A debugger that cannot replay the same events accepted by the firewall is not trustworthy.

A future desktop app is part of the memory debugger/control-plane surface, not a new runtime or storage boundary. It must consume the same receipts and call the same CLI/MCP/daemon/future HTTP write path rather than writing events, provider-native memory, or database rows directly. See ADR 0020.

The integrity semantics behind these surfaces have a formal identity — dent8 is a belief base with paraconsistent contradiction tolerance and an authority-as-entrenchment ordering. See belief-revision.md. The adversary the firewall defends against is in threat-model.md, and how the invariants are checked is in formal-verification.md.

The durable storage design is the append-only event log, its projection, the edge graph, and a tamper-evident hash chain — expressed against the EventStore / AsyncEventStore traits. Postgres and embedded SQLite are adapters that realize it, not the architecture — both implement the async boundary and share the same firewall and hash chain. The full design (backend-agnostic event log + adapters + canonicalization) lives in storage.md; the original Postgres-first choice is ADR 0001, with the SQLite backend added later as the second adapter that proves the boundary holds. DuckDB and Parquet are an export-only analytical lane that consumes exported event streams, never runtime writes — built as dent8-export / dent8 export (see storage.md).

dent8/
crates/
dent8-core/ # fact-event model, lifecycle state machine, invariants
dent8-store/ # store traits (sync EventStore + async AsyncEventStore), replay boundary
dent8-store-postgres/ # Postgres migrations and adapter (AsyncEventStore)
dent8-store-sqlite/ # embedded SQLite adapter (AsyncEventStore) — the second backend
dent8-cli/ # CLI commands for schema, replay, explain, MCP
dent8-evals/ # adversarial corpus behind the self-demonstrating `dent8 eval`
dent8-export/ # Parquet export for DuckDB analysis (opt-in, `--features export`)
crates/dent8-store-postgres/migrations/postgres/
# SQL migrations packaged with the Postgres crate
docs/ # architecture, eval strategy, naming, MVP notes
evals/
fixtures/ # canonical event streams
replay/ # replay scenarios and expected outcomes

Later crates should be added only when they own a real boundary:

  • dent8-policy: write/read policy and approval state machines.
  • dent8-mcp: MCP transport adapter.
  • dent8-http: HTTP API.
  • dent8-debugger: debugger query model before the TypeScript/Tauri desktop UI exists.

The primitive is FactEvent.

Each event belongs to a fact stream identified by fact_id. A fact stream starts with fact.asserted; later events can reinforce, contradict, supersede, expire, retract, retrieve, use the fact in a decision, or record a rejected challenge (fact.challenge_rejected, ADR 0015).

Core fields:

  • event_id
  • fact_id
  • event_type
  • subject
  • predicate
  • fact_value
  • confidence
  • authority
  • ttl
  • provenance
  • evidence
  • observed_at
  • valid_from
  • valid_to
  • recorded_at
  • causation_event_id
  • correlation_id
  • event_hash

The first projection state machine is intentionally small:

none -> active
active -> contested
active -> superseded
active -> expired
active -> retracted
contested -> superseded
contested -> expired
contested -> retracted

Retrieval and decision-use events are audit events. They do not change lifecycle state but they matter for debugging stale or unsafe context use. On the CLI, dent8 context --record-retrieval emits fact.retrieved for every fact it packs, and a dent8 capture proposal with "op": "used_in_decision" records fact.used_in_decision. MCP resources/read auto-emits fact.retrieved (purpose mcp:resources/read; opt out with DENT8_MCP_RECORD_RETRIEVAL=0); the MCP server also renders both event kinds in replay/explain (see STATUS.md).

  1. Normalize input into a candidate fact event.
  2. Validate required provenance, evidence, authority, TTL, and schema shape.
  3. Read relevant active/contested projections for the same subject and predicate.
  4. Detect duplicate, contradiction, or supersession candidates.
  5. Accept, reject, or require explicit policy approval.
  6. Append the immutable event in Postgres.
  7. Update projections in the same transaction.
  8. Emit invariant results for replay and debugger use.

Reads should return fact state plus integrity metadata:

  • lifecycle state
  • freshness and TTL
  • authority level
  • supporting evidence
  • contradiction count
  • supersession chain
  • provenance summary
  • replay position

The MCP and CLI surfaces should expose this metadata by default. dent8 should make unsafe or stale memory visible, not hide it behind a plain string context blob.

The core fold implements lifecycle transitions, terminal immutability, contradiction-as-contested, canonical-contradiction hard alarms, and authority-weighted terminal mutation: lower-authority supersession, explicit expiration, and retraction are rejected (ADR 0007, ADR 0011). The CLI/MCP write surface runs through the same firewall path, with an additional source->authority ceiling registry at the op_* layer, plus an optional pluggable content-check hook at that same layer (content-check.md): arbitration never reads value text, so a deployment composes an external scanner in — dent8 ships no classifier of its own. The Postgres adapter commits accepted events transactionally under an advisory-lock-serialized append, and the read surface applies freshness by flagging stale receipts. Remaining product gaps are operational: signed identity key distribution/rotation, an operated witness service, richer transports, and more live Postgres smoke coverage. See STATUS.md, roadmap.md, and threat-model.md.