Memory that takes a fact back.
Vayl is a self-hosted memory layer for AI agents, over MCP. It reconciles instead of accumulates: a new value supersedes the old, a removal actually retracts, ambiguity is flagged rather than guessed — and every control on the trust layer is verifiable by command.
Append-only memory serves stale answers.
Two failure modes, shown as output rather than argued in prose. Both stores did their job as designed — the design is the problem.
> add("We use Zustand for state.") > add("We switched to Redux. Zustand is out.") > search("what do we use for state?") 1. "We use Zustand for state" 0.91 ← stale, ranked first 2. "We switched to Redux …" 0.86
Every fact is retained; similarity ranking decides the truth. The value you explicitly moved away from comes back first.
> add("We use Sentry for error monitoring.") > add("We dropped Sentry last month.") > search("what do we use for monitoring?") USES_MONITORING(org → Sentry) valid: true ← never invalidated
Graphs reconcile contradictions well — a new edge invalidates the old one. But a removal creates no new edge, so there is nothing to invalidate against. “We dropped X” leaves X standing.
A real change retires the old value into history and makes the new one current.
A removal with no replacement empties the slot. A tombstone keeps the provenance; recall never returns the retired value.
Same slot, same scope, no change signal — genuinely ambiguous. Surfaced for a human, never guessed.
Questions, hypotheticals, sarcasm. Not durable facts, never stored as truth.
Mechanism: one LLM call extracts and classifies, the engine applies the outcome. No graph database, no second system of record.
Numbers, with the counter-entries.
Each figure carries the command that reproduces it. The counter-entries are part of the statement, not a footnote.
800 fact-updates across 50 users, same model, same synthesizer. Additive memory keeps every version and returns a stale value one lookup in three (32.5%); Vayl retires superseded facts on write, so its active set stays true — and stores 199 rows where an additive store holds 800. See the full benchmark →
python benchmarks/evaluations/scale_bench.py
Eleven relational queries — ownership chains, transitive dependencies, 3-hop supply — the axis graphs are built for. Vayl's optional graph holds 0% silently-wrong and 8/11 correct (both 3-hop chains), delivered as an SQLite projection, not a required Neo4j deployment. See the full benchmark →
python benchmarks/evaluations/graph_headtohead.py
“We dropped Sentry” must stop being returned. Vayl retires the fact on write — kept in history for audit, gone from the current set. An additive store keeps it searchable, so a stale value can resurface. Measured in the cross-system retract scenarios.
python benchmarks/evaluations/compare_systems.py
The one number that kills a memory product: returning a stale or wrong value as if it were current. 255 adversarial trials on Claude Haiku. Ambiguity resolves to FLAG, not to a guess.
python benchmarks/evaluations/eval_adversarial.py
Extract and reconcile against a single SQLite file — no graph pipeline, no Neo4j to operate. Vayl’s writes run ~2.8 s, versus the ~6–9 s typical of a graph pipeline (a graph server plus multi-step edge extraction).
python benchmarks/evaluations/compare_systems.py
On ordinary “X changed to Y” supersession with a capable model, an additive store plus a good reader keeps up. Vayl’s measured edge is removal, honesty under ambiguity, cost, and holding up under sustained churn — not a single clean swap.
python benchmarks/evaluations/compare_systems.py
The engine is only as honest as the model reading the input. On the default gpt-5-mini, the messy real-world suite — typos, slang, self-corrections, multi-fact messages across a dozen domains — returns 0% silently-wrong. A weaker model degrades; measure yours before you trust it.
OPENAI_MODEL=gpt-5-mini python benchmarks/evaluations/messy_eval.py
Single-run, author-written suites — not third-party audits. That is exactly why every row above ships with its command: run them before you rely on them.
ls benchmarks/
Twenty-nine tools, five families.
The full MCP surface. Every mutation keeps history; every destructive tool is annotated so clients confirm before running it.
Core memory 8 tools› Write, reconcile, and read the current truth — plus the history additive stores can’t give you.
rememberstore fact(s); supersedes, retracts, or splits multiple facts per message; carries a sourcerecallanswer from current memory; says “I don’t know” rather than guessingrecall_relatedmulti-hop questions via the optional entity graphhistoryevery value a subject has held, oldest to newest, with statusget_memoryone fact’s structured detail by idupdate_memoryaudit-preserving correction — the old value retires to historyforgetguaranteed retraction, retained in history for auditlist_memoriesactive facts plus what was superseded and retractedProvenance & decisions 3 tools› Why does the agent believe this — and why did it act? Answerable even after the facts change.
recall(explain)the exact facts behind an answer: value, source, confidence, what they supersededrecord_decisionan immutable, signed snapshot of the beliefs behind an actionexplain_decisionreconstruct what was believed at decision time, receipt verifiedSafety gates 2 tools› Refuse to act on disputed, stale, or low-confidence memory. Reads stay open; actions get gated.
check_before_actSAFE, or BLOCKED with explicit reasons, before an irreversible actionsafe_recallanswers only if every supporting fact passes the policy; otherwise withholds and says whyCompliance & proof 9 tools› Hard erasure with signed receipts, attestations, and a tamper-evident audit chain — all third-party verifiable.
delete / delete_allhard erasure including history and graph; issues a signed erasure receiptpurge_expiredretention by age — storage-limitation enforcementexport_memorymachine-readable dump for access and portability requestsattestsigned proof of the current value, anchored to the audit headverify_receiptVALID, or INVALID if any field was editedverify_auditINTACT, or the exact row where the chain breaksexport_public_keyverify receipts and the chain without the database or the secretaudit_logwho did what, when — encrypted at rest, never wiped by erasureAdmin 8 tools› Principals, roles, shared-space policy, edition, and health — the team-deployment surface.
create_principalissue an API key with a role: admin, member, agent, viewer, auditorlist_principals / revoke_principalsee and disable access; revocation is immediateset_reconcile_policy / get_reconcile_policyshared spaces: RECENCY, AUTHORITY, or REVIEW — a lower-ranked source can’t silently overwrite a higher onelicense_statusedition, seats used vs allowed, expirystats / healthon-device metrics and a setup diagnostic; nothing leaves the machineTwo minutes to a running memory. Point it at your model provider — your key, your data-residency posture; the database on disk stays encrypted either way.
# install
git clone https://github.com/vayl-dev/vayl && cd vayl
pip install .
// Claude Desktop / Cursor → mcpServers "vayl": { "command": "vayl-mcp", "env": { "LLM_PROVIDER": "openai", "OPENAI_API_KEY": "sk-…", "OPENAI_MODEL": "gpt-5-mini", "EMBED_BASE_URL": "https://api.openai.com/v1", "EMBED_MODEL": "text-embedding-3-small", "VAYL_DB": "/absolute/path/vayl.db" } }
The default, and the one the numbers above were measured on: 0% silently-wrong on the messy real-world suite. Encryption at rest and the signed audit chain are on by default.
# install
git clone https://github.com/vayl-dev/vayl && cd vayl
pip install .
// Claude Desktop / Cursor → mcpServers · Claude reconciles, OpenAI embeds "vayl": { "command": "vayl-mcp", "env": { "LLM_PROVIDER": "anthropic", "ANTHROPIC_API_KEY": "sk-ant-…", "EMBED_BASE_URL": "https://api.openai.com/v1", "EMBED_API_KEY": "sk-…", "EMBED_MODEL": "text-embedding-3-small", "VAYL_DB": "/absolute/path/vayl.db" } }
Reconciliation runs on Claude; embeddings on any OpenAI-compatible endpoint (Anthropic has no embeddings API). For a shared team endpoint over authenticated HTTP, see the deployment runbook in the footer.
Verify it. Don’t take the page’s word.
Every claim below is a command and its output, paired with the test that pins the behavior in CI. Adjectives don’t survive an audit; these do.
The audit log is tamper-evident.
Each entry hashes its predecessor and is Ed25519-signed. Edit, delete, or reorder any row and verification names the exact break.
tests/test_audit.py::test_tampering_a_detail_breaks_the_chain
> verify_audit Audit chain INTACT — 218 entries verified. # an attacker edits row 2 in the database, then: > verify_audit Audit chain BROKEN at seq 2: content tampered (a row’s stored bytes were altered)
Erasure produces a receipt anyone can check.
Deleting a subject issues a signed receipt — scope, subject, row count, chain anchor. Change any field and the signature fails.
tests/test_receipts.py::test_editing_any_payload_field_invalidates_the_receipt
> delete subject=alice_salary Erased 3 record(s). Signed erasure receipt #14 issued. > verify_receipt 14 VALID — signature verified, payload intact # edit the receipt’s count from 3 to 0, then: > verify_receipt 14 INVALID — signature does not match
The data on disk is ciphertext.
Content columns are Fernet-encrypted at rest, on by default. Search the raw database file for a stored value and find nothing.
tests/test_crypto.py::test_data_is_ciphertext_at_rest_but_plaintext_on_read
$ strings vayl.db | grep 120000 (no matches) $ sqlite3 vayl.db "SELECT value FROM statements LIMIT 1" gAAAAABqXGc8t4v… # ciphertext, not the salary
Third parties verify without the secret.
Receipts, attestations, and the audit chain check out against the public key alone — no database access, no key material, no trust in the operator.
tests/test_receipts.py::test_erasure_receipt_verifies_with_public_key_only
> export_public_key ed25519: 727b94c14f7abecd188a99a74b5b7ec9… # on any other machine, with only the key and the receipt: > receipts.verify(receipt, public_key) True
Editions.
Community
Apache-2.0 · free · self-hosted- reconciling engine — supersede / retract / flag / skip
- MCP over stdio + HTTP — Claude Desktop, Cursor, Claude Code
- encryption at rest — on by default
- signed audit chain, receipts, attestations
- safety gates, hard erasure, export
- SQLite backend — single deployment
- capped seats — 3 principals
Enterprise
self-hosted license · signed offline, works air-gapped- everything in Community, seats uncapped
- SSO / OIDC — your IdP’s tokens, roles mapped from claims
- Postgres backend — multi-process, advisory-locked writes
- HashiCorp Vault key custody — fail-closed
- multi-tenant isolation seam
- source-aware shared memory — RECENCY / AUTHORITY / REVIEW
- support with SLA, compliance pack