# dent8
**A memory firewall for coding agents.** dent8 sits between your agent and its long-term
memory and refuses the writes that quietly corrupt it — a low-authority source overwriting a
trusted fact, a stale value resurrecting itself, a poisoned source tainting everything
derived from it. Every fact it keeps carries where it came from, and you can replay exactly
*why* the agent believes it.

Most agent memory is *newest-write-wins*: the last thing written becomes the truth. That's
how a scraped web page silently overwrites a decision your team made, and how a retracted
source leaves its conclusions behind. dent8 treats memory as an **append-only log of fact
events** — each carrying provenance, authority, and evidence — and **arbitrates every write**
against what is already believed.
## The 30-second proof
```sh
cargo install dent8
dent8 eval
```
`dent8 eval` runs an adversarial corpus against the real firewall **and** a recency-only
baseline (newest-write-wins — the resolution Zep/Graphiti use):
| low-authority memory injection (MINJA) | blocked ✓ | **compromised** |
| authority laundering | blocked ✓ | **compromised** |
| canonical contradiction | blocked ✓ | **compromised** |
| Sybil corroboration | blocked ✓ | **compromised** |
| poisoned-source retraction | blocked ✓ | **compromised** |
Five for five. The last one is the tell: retract a poisoned source and dent8 flags every fact
*derived* from it — a dependency cascade a recency-only store structurally cannot express.
## Try it
```sh
dent8 init --identity --source source:alice # local setup: env + authority + signed identity
set -a; . .dent8/env; set +a
. .dent8/identity-alice.env
dent8 authority add web:scrape low # let this source write at Low, so the next rejection
# is about arbitration, not a missing grant
# A trusted fact goes in.
dent8 assert repo:myproj deploy_target production
# A low-authority source tries to overwrite it — the firewall rejects it: Low can't override High.
dent8 supersede repo:myproj deploy_target staging --authority low --source web:scrape
# It is still production, and here is the receipt that proves why.
dent8 explain repo:myproj deploy_target
# Derive a fact from it, then retract the source — the derivative is flagged tainted.
dent8 derive service:api target production --basis repo:myproj deploy_target
dent8 retract repo:myproj deploy_target
dent8 verify
```
No services required — dent8 uses a local file log by default. From a clone, watch the whole
firewall path run through the real CLI:
**`DENT8="cargo run -q -p dent8 --" ./examples/firewall/demo.sh`**.
## How it works
The primitive is a **fact event**, not a generic memory item. Every write that enters dent8 is
arbitrated at the append boundary (`EventStore::append`) before it is persisted:
- **Authority-weighted arbitration** — a write cannot override a fact of higher authority, and
dent8 checks the *actual* authority behind a revision, not just the authority claimed by the
event, so laundering a weak fact through a high-authority-looking event is caught.
- **Paraconsistent contradiction** — disagreement is *kept* as a contested pair rather than
silently resolved; but contradicting a `canonical` fact is a hard alarm, not a soft contest.
- **Earned entrenchment** — a fact backed by more independent sources, or one that has
*survived challenges* (rejected attacks are recorded on the fact itself), becomes harder to
displace.
- **Freshness & valid-time** — facts carry TTLs and asserted validity windows; reads flag
stale and not-yet-valid facts, and can **time-travel** ("what did we believe last Tuesday,
and was it fresh then?").
- **Tamper-evidence** — a SHA-256 hash chain over the log, plus an optional off-host
**witness** (Ed25519 signed tree heads) that catches a history rewrite an internal re-check
cannot.
- **Signed identity** — optional issuer-signed grants bind a source to a key; every accepted
write carries a signature `verify` re-checks offline, backed by a hash-chained grant history
with first-class **revocation**.
Nothing is a black box: `dent8 replay` shows the full event history behind any fact, and
`dent8 verify` re-checks integrity, supersession lineage, and retraction taint.
## Use it with your agent
dent8 speaks MCP, so agents read and write memory *through the firewall*:
```sh
dent8 init --agent codex --install-mcp # signed identity + MCP config for Codex
dent8 doctor --agent codex --write-check # smoke the installed server + prove the firewall path
dent8 doctor --all-agents --write-check # check every installed agent profile in this bundle
```
Shortcuts exist for `codex`, `claude-code`, `cursor`, `gemini`, `grok-build`, `cascade`, and
`hecate`, and several agents can share one belief base over a common backend. See
[examples/mcp/](examples/mcp/) and the per-agent example directories, or wire dent8 in over
MCP from [LangChain](examples/langchain/) / the [Vercel AI SDK](examples/vercel-ai-sdk/).
### Share one belief base over a daemon
Instead of one dent8 process per agent, run a **per-user daemon** and point writes at it:
```sh
dent8 mcp serve --daemon # Unix socket at $XDG_RUNTIME_DIR/dent8/dent8.sock
export DENT8_DAEMON_SOCKET="$XDG_RUNTIME_DIR/dent8/dent8.sock"
dent8 doctor # check the daemon is reachable and you authenticate
dent8 assert repo:app deploy_target production
```
Every connection proves the daemon-configured source identity with a signed session challenge,
and the daemon arbitrates and **attests each write as that source** — so a daemon-written fact
re-verifies offline exactly like a local one, and many processes build one firewalled belief base
over one transport. Reads stay local. Run the whole path with
**`DENT8="cargo run -q -p dent8 --" ./examples/daemon/demo.sh`** (see
[examples/daemon/](examples/daemon/)). Today each daemon process is single-source: every
client connection must prove the same source key the daemon holds, so this shares *one*
identity across processes. Separate agent identities should use separate MCP subprocesses
against the same backend, or separate daemon instances.
## Status
dent8 is **pre-1.0 and experimental** — the CLI, the library API, the on-disk event format, and
the Postgres storage schema may all still change between minor versions. The event format is
**versioned** by `CANON_VERSION`, mixed into every hash so encodings can never collide. v0.3
introduces **format v2** (the `fact` vocabulary — `claim_id` → `fact_id`, lowercase `authority`);
this is a deliberate one-time pre-1.0 break, so a v1 log does not carry forward and must be
re-ingested from source. See [the v0.3 upgrade note](docs/upgrading-to-v0.3.md). From v2 onward
the intent is again additive-only (new fields stay optional and out of the hash), but that
stability is not guaranteed until 1.0.
[docs/STATUS.md](docs/STATUS.md) is the single source of truth for what is runnable vs.
library-only vs. design-only. In brief, runnable today:
- The full belief lifecycle — `assert` / `supersede` / `retract` / `contradict` / `reinforce`
/ `expire` / `derive` / `explain` / `replay` — plus the operator surfaces `facts list`,
`verify`, `conflicts`, `eval`, and `export`.
- Three backends behind one contract: a local **file** dev log (default), embedded **SQLite**,
and a DB-verified transactional **Postgres** adapter (`--features postgres`) — selected by
`DENT8_STORE_URL`.
- **Signed identity** with grant history + revocation, the **witness** transparency log, and an
MCP server (`dent8 mcp serve`) — all in the stock binary.
- MCP `runtime_status` diagnostics so agents can see the live binary, store, identity,
authority, and witness configuration before trusting a long-running server.
- Machine-readable `--output json` across the read/write/audit surface, shell completions, and
Parquet **export** for offline DuckDB analysis (`--features export`).
- Read-only native memory/rules audit with `dent8 native scan --agent <profile>` and receipt
verification with `dent8 native reconcile --agent <profile>`; MCP exposes the same audits as
`native_scan` / `native_reconcile`. Native import/export remain future work.
Run `dent8 --help` for the full command surface. The stock binary needs no services; opt-in
builds add a Postgres backend (`--features postgres`) and Parquet export (`--features export`).
The future desktop app is scoped as a debugger/control plane over these same surfaces, not a
separate memory provider ([ADR 0020](docs/decisions/0020-desktop-debugger-control-plane.md)).
*(Origin: the* dentate gyrus*, the hippocampal structure associated with pattern separation —
keeping similar memories distinct.)*
## Documentation
**Start here**
- [Implementation Status](docs/STATUS.md) — single source of truth for what is built
- [Configuration](docs/configuration.md) — every env var and Cargo feature
- [Changelog](CHANGELOG.md)
**Design**
- [Project Brief](docs/project-brief.md) · [Architecture](docs/architecture.md) ·
[Domain Model](docs/domain-model.md)
- [Belief Revision](docs/belief-revision.md) — dent8's formal identity (the lead lens)
- [Storage & the Event Log](docs/storage.md) · [Interfaces](docs/interfaces.md) ·
[Naming](docs/naming.md)
- [Decision Records](docs/decisions/)
**Correctness & security**
- [Threat Model](docs/threat-model.md) — exactly what the firewall does and does not defend
- [Formal Verification](docs/formal-verification.md) · [Evaluation Strategy](docs/evals.md)
**Planning & research**
- [Roadmap](docs/roadmap.md) · [Release Checklist](docs/release.md) ·
[Related Work](docs/related-work.md)
- [Research Dossier](docs/research/dossier.md) ·
[Open Directions](docs/research/novelty.md) ·
[Paper Outline](docs/paper/outline.md)
## Development
```sh
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
DENT8="cargo run -q -p dent8 --" ./examples/firewall/demo.sh
# The Postgres adapter's integration tests are gated on DATABASE_URL (they skip without one):
docker compose up -d
DATABASE_URL=postgres://postgres:dent8@localhost:5432/dent8 \
cargo test -p dent8-store-postgres --features adapter
docker compose down
```
CI ([`.github/workflows/ci.yml`](.github/workflows/ci.yml)) runs the fmt/clippy/test gate
across feature combinations and the adapter against a live Postgres service. Security reports:
see [SECURITY.md](SECURITY.md).
## License
Licensed under either of [Apache-2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT) at your option.
Unless you state otherwise, any contribution you intentionally submit for inclusion in the
work, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms.