Memora
Catch your AI citing sources that don't say what it claims. Cite, or it didn't happen.
Memora retrieves claims, not notes - atomic facts with source-span pointers, validity windows, and privacy bands. When the model answers, Memora re-reads the verbatim source span behind every citation and recomputes its blake3 hash; citations the source doesn't contain are rejected before the answer reaches you. Hallucinated citations are caught structurally, not by trust.
→ See the architecture in motion
The problem
Personal AI memory tools, from RAG over Obsidian to second-brain wrappers, share one weakness: they retrieve notes and trust the LLM to quote them faithfully. When the LLM fabricates a meeting that didn't happen, puts words in someone's mouth, or cites a claim your notes don't contain, you have no architectural defense. You either catch the hallucination yourself, or you don't.
For a personal knowledge base with decisions and meeting notes, that is the wrong trust model.
How Memora differs
The atomic unit of memory is a claim, not a note. A claim is an extracted statement of fact with:
- subject, predicate, object
- source note + byte-range span
- blake3 fingerprint of the source text
- valid_from / valid_until temporal window
- privacy band (public / private / secret)
- provenance edges to source claims when synthesized
When the LLM answers, it cites claim ids. The validator re-reads the source span from your markdown, recomputes the fingerprint, and rejects citations that don't match. Hallucinated ids get stripped and the LLM is re-prompted with verified-only context. The citation contract is enforced by Rust types and span hashes, not by prompt obedience.
What it guarantees - and what it does not
- Provenance integrity (guaranteed): the cited source span verbatim exists, is unmodified (hash-proven via full-width blake3), and contains any quoted text. This is strictly stronger than model-asserted citation APIs.
- Entailment (optional, not guaranteed): whether the source actually supports
the conclusion (rather than just containing the quote) is a separate, harder
question.
memora verify --entailmentruns an opt-in, LLM-judged check for it, kept clearly separate from the hash-proven provenance.
What you get
| Verified citations | Every claim_id in an answer is re-validated against the source span before reaching you. Hallucinations stripped. |
| Provenance + staleness | Synthesis claims point to sources. Edit a note, downstream syntheses are auto-marked stale. |
| Time-aware reasoning | Claims have validity windows. "What was true in March" is queryable. Contradictions auto-supersede. |
| Per-claim privacy | Inline <!--privacy:secret-->...<!--/privacy--> markers. Secrets redacted at the wire boundary on cloud LLMs, type-system enforced. |
| Active challenger | Daily background worker surfaces stale claims, contradictions, cross-region patterns, and frontier gaps in your world_map.md. |
| Document ingestion | memora ingest pulls PDFs, web pages, text, and transcripts into the vault as verifiable notes (PDF/web behind opt-in features). |
| Visual report | memora report renders the vault — interactive claim graph, contradictions, stale dependencies, world map — as one offline HTML file. |
| Optional entailment | memora verify --entailment adds a best-effort, LLM-judged check that the source supports the claim, separate from the proof. |
| Hybrid retrieval | BM25 + embedding + reciprocal-rank fusion. |
| Local-first | Single Rust binary. SQLite + HNSW. Works fully offline with Ollama or a bundled on-device embedder. |
| Obsidian-native | Plain markdown vault with frontmatter. Open and edit in Obsidian alongside Memora. |
| MCP-native | Drop into Claude Code, Cursor, or any MCP client over stdio. |
Where to go next
- Quickstart - install and first verified citation in 10 minutes.
- Ingesting documents - bring PDFs, web pages, and transcripts into the vault.
- Architecture - claim graph, retrieval, validation pipeline.
- Obsidian guide - daily-driver setup with Claude Code.
- Comparison - honest breakdown vs Mem0, Zep, Letta, and Anthropic Citations.
- MCP tools - every tool, with examples.
Status
v0.1.29. Indexes 100-note vaults in 5 to 10 minutes with Claude Haiku for about $0.30. Local Ollama is supported. Vault sizes up to a few thousand notes are the target. Larger scales are unmeasured. The active challenger surfaces decisions, contradictions, stale dependencies, and open questions in every atlas. MCP and watch now share the same embedder config, privacy redaction, and claim extraction pipeline as the CLI.
Issues, edge cases, and design discussions welcome at github.com/radotsvetkov/memora/issues.
Quickstart (10 Minutes)
Catch your AI citing sources that don't say what it claims. Cite, or it didn't happen.
Memora retrieves claims, not notes - atomic facts with source-span pointers, validity windows, and privacy bands. Every LLM citation is architecturally validated against your markdown.
This walkthrough takes a determined user from install to first verified cited answer in about 10 minutes.
1) Install Memora
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/radotsvetkov/memora/releases/latest/download/memora-cli-installer.sh | sh
What just happened
The installer places the Memora binaries on your system path (typically including
memora and memora-mcp), so you can run CLI commands and expose MCP tools to
clients without manual build steps.
2) Initialize a vault
memora init --vault ~/brain
What just happened
Memora created a vault scaffold with:
world_map.md(generated memory overview surface)- a sample semantic region folder
.memora/config.tomlfor local configuration and sidecar state
Your markdown vault stays human-readable and Obsidian-compatible from the start.
3) Add your first note manually
Create ~/brain/semantic/projects/drift/roadmap.md:
---
title: drift Serialization Decision
type: semantic
date: 2025-09-12
region: projects/drift
privacy: private
tags: [drift, serialization, architecture]
---
drift switched its serialization format from JSON to MessagePack in Q3.
Benchmarking showed a 3x throughput improvement.
Decision recorded in roadmap, retro, and review notes.
What just happened
You wrote plain markdown with frontmatter. No proprietary format is required. Memora will use this as extraction input and anchor future citations to precise source spans inside this file.
4) Index the vault
memora index --vault ~/brain
What just happened
Indexing runs a full pipeline:
- note metadata parsed and recorded
- claim extraction generates atomic claim candidates
- each stored claim is linked to source byte range and fingerprinted
- lexical and vector indexes are updated
- contradiction checks compare new/current claims and supersede older conflicts
The result is a queryable claim graph rather than a plain chunk index.
5) Ask your first query
memora query "What did we decide about drift's serialization format?" --vault ~/brain
What just happened
Memora retrieved claims, assembled model context, generated an answer, and validated cited claim ids against source spans before returning output. In the answer footer, citation entries correspond to claim ids that survived validation.
6) Add Claude Code MCP integration
Add one server entry to your MCP config:
{"mcpServers":{"memora":{"command":"/usr/local/bin/memora-mcp","env":{"MEMORA_VAULT":"/absolute/path/to/brain","MEMORA_ENABLE_NETWORK_LLM":"1"}}}}
What just happened
Claude Code can now call Memora tools over stdio MCP. Instead of passing raw note chunks manually, you get memory-tool access with claim-aware retrieval and built-in citation validation behavior.
7) Ask the same question in Claude Code
Prompt:
What did we decide about drift's serialization format?
What just happened
Claude Code routed the request through Memora MCP tools. The response surfaced with verified citations tied to your markdown-backed claims. If a citation fails validation, Memora strips/retries with verified-only context before returning.
Next 5 minutes (recommended)
- Run
memora watch --vault ~/brainin a dedicated terminal tab. - Add inline secret markers in mixed-sensitivity notes.
- Check
world_map.mdafter your next capture session.
Troubleshooting basics
command not found: memora-> restart shell or verify installer path export.- No claims extracted -> confirm frontmatter + note content + successful indexing.
- MCP tool unavailable -> verify
memora-mcppath andMEMORA_VAULTvalue. - Sparse answers -> add more factual statements; reflection-only notes yield fewer claims by design.
Recommended Models
Memora makes two kinds of LLM calls. Extraction runs once per note and produces structured triples. Synthesis runs once per atlas and produces prose. Provider quality matters, especially for extraction.
Anthropic Claude Haiku (recommended)
This is what Memora was tuned against. Best balance of cost and quality.
[llm]
provider = "anthropic"
model = "claude-haiku-4-5-20251001"
Cost: about $0.30 to index a 100-note vault.
Speed: 5 to 10 minutes with parallelism = 8.
Anthropic's free tier limits requests to 50 per minute. Add at least $5 of credit to reach Tier 1, or set parallelism = 1 to stay under the free limit.
OpenAI gpt-5-mini (alternative)
Comparable extraction quality at similar cost.
[llm]
provider = "openai"
model = "gpt-5-mini"
Local (Ollama)
Use this when local-only is a hard requirement.
[llm]
provider = "ollama"
model = "qwen2.5:32b-instruct-q5_K_M"
Honest assessment:
- Qwen 14B is insufficient for production (hallucinates relationships, shallow triples).
- Qwen 32B is acceptable but misses cross-region patterns.
- Llama 70B can match Haiku quality with a large memory cost.
- Below 32B parameters, atlas synthesis quality drops noticeably.
Embeddings run locally regardless of chat provider:
[embed]
provider = "ollama"
model = "nomic-embed-text"
dim = 768
For a fully on-device embedder with no Ollama daemon, build with
--features local-embed and use the bundled fastembed BGE-small model:
[embed]
provider = "local"
dim = 384
provider = "openai" is also supported (cloud), gated behind
MEMORA_ENABLE_NETWORK_LLM=1 like the cloud chat providers.
Ingesting documents
Memora verifies citations against the byte spans of markdown notes. To make an
external document verifiable, you turn it into a vault note first. memora ingest
does that: it extracts clean text from the source, writes a note with valid
frontmatter under a region you choose, and then the normal pipeline (index →
extract claims → verify) treats it like any other note.
memora ingest meeting-notes.txt --vault ~/brain
memora ingest interview.vtt --vault ~/brain --region interviews
memora ingest contract.pdf --vault ~/brain --region legal # needs the pdf feature
memora ingest https://example.com/post --vault ~/brain --region web # needs the web feature
After ingesting, index the vault so the claims become verifiable:
memora index --vault ~/brain
Supported formats
| Format | Extensions | Notes |
|---|---|---|
| Plain text | .txt, .text | Read as-is. |
| Markdown | .md, .markdown | Read as-is. |
| Transcripts | .vtt, .srt | Cue numbers, timestamps, and the WEBVTT header are stripped; spoken text is kept. |
.pdf | Text extraction via pdf-extract. Requires the pdf feature. | |
| Web page | a URL, or .html/.htm | Readable text (paragraphs, headings, lists, quotes, code) and the page title, via scraper. Scripts, styles, and most navigation are dropped. Requires the web feature. |
Optional features (PDF and web)
PDF and web support are behind Cargo features so the default binary and its supply chain stay lean. Enable what you need:
cargo install memora-cli --features pdf # PDF
cargo install memora-cli --features web # URLs and .html files
cargo install memora-cli --features "pdf web" # both
Without the matching feature, memora ingest fails with a clear message rather
than silently doing nothing. Notes:
- Scanned (image-only) PDFs have no extractable text; run OCR first and ingest the result.
- Web extraction is best-effort; it keeps the main content but may miss or include some chrome. Edit the resulting note in Obsidian to trim anything unwanted before indexing.
What the note looks like
- id — a readable slug from the filename or URL plus a short hash of the source, so re-ingesting the same source updates the same note instead of duplicating it.
- source —
reference(an external document, not your own writing). - region —
--region(defaultingested). - privacy —
--privacy(defaultprivate); usesecretfor sensitive documents so their content is redacted before any cloud call. - summary — the first non-empty line, falling back to the filename.
The body is the extracted text, lightly normalized (control characters removed, long runs of blank lines collapsed). You can edit it in Obsidian afterward like any other note.
Vault Conventions
Memora reads markdown notes from your vault and writes derived artifacts under .memora/.
Frontmatter (recommended)
Memora works with plain Obsidian-created notes that have no frontmatter.
On first memora index or memora watch, it auto-fills any missing fields
and prepends a YAML block while preserving your original note body byte-for-byte.
You can still provide and manage frontmatter manually. Memora applies a few quality-of-life normalizations while indexing:
regionfollows the note's folder path relative to the vault root.updatedfollows the file mtime (second precision).refscan optionally mirror detected wikilinks (configurable).- Invalid
source/privacyenum values are normalized to defaults.
---
id: note-id
region: projects/drift
source: personal
privacy: private
created: 2026-04-01T00:00:00Z
updated: 2026-04-01T00:00:00Z
summary: "Short summary"
tags: [tag1, tag2]
refs: []
---
Example inferred frontmatter for an Obsidian note semantic/projects/drift/serialization-note.md:
---
id: serialization-note
region: projects/drift
source: personal
privacy: private
created: 2026-04-30T18:42:10Z
updated: 2026-04-30T18:42:10Z
summary: "drift switched from JSON to MessagePack after benchmarking"
tags: []
refs: []
---
Inline privacy markers
Use markers to narrow privacy to a sub-span:
This is public context.
<!--privacy:secret-->
Salary is 120k.
<!--/privacy-->
Claims extracted from secret spans are marked secret even when note privacy is broader.
Derived files
world_map.md<region>/_atlas.md<region>/_index.md.memora/config.toml.memora/memora.db.memora/vectors/*.memora/last_challenger.json.memora/watch.lock(present whilememora watchis running)
Derived markdown can be regenerated and should not be edited as source-of-truth notes.
Frontmatter config knobs
In .memora/config.toml:
[frontmatter]
refs_mode = "sync_from_wikilinks" # or "manual"
sync_from_wikilinks: keeprefsaligned to[[wikilinks]]in body.manual: never auto-rewriterefs.
Obsidian Properties Template
Use this template in Obsidian so source and privacy are always visible and quick to edit.
Suggested property options
source:personal,reference,derivedprivacy:public,private,secret
Recommended defaults:
source: personalprivacy: private
Example note template
---
id: "{{title}}"
region: default
source: personal
privacy: private
created: 2026-01-01T00:00:00Z
updated: 2026-01-01T00:00:00Z
summary: ""
tags: []
refs: []
---
Notes
- Memora keeps timestamps in second precision (
...T12:34:56Z). - If
frontmatter.refs_mode = "sync_from_wikilinks",refsis rewritten from body wikilinks. - Invalid
source/privacyvalues are normalized topersonal/private.
Obsidian + Memora Daily Driver Guide
Catch your AI citing sources that don't say what it claims. Cite, or it didn't happen.
Memora retrieves claims, not notes - atomic facts with source-span pointers, validity windows, and privacy bands. Every LLM citation is architecturally validated against your markdown.
This guide is for Obsidian users who want Memora as a local-first memory backend while using Claude Code as the conversational interface.
What this guide assumes
- You already keep (or want to keep) a markdown vault.
- You want to preserve prose as prose, not force everything into rigid fields.
- You want model answers that are grounded in verifiable source spans.
- You want a practical workflow you can run daily with minimal friction.
Recommended Vault Layout
Use a structure that keeps note intent clear and lets Memora infer context.
~/brain/
episodic/
meeting-2025-q3-arch.md
2025-10-design-review.md
semantic/
projects/
drift/
roadmap.md
serialization-strategy-2025-q3.md
drift-bench/
benchmark-language-notes.md
people/
sarah.md
james.md
priya.md
procedural/
release-checklist.md
incident-playbook.md
world_map.md
.memora/
config.toml
state.db
logs/
Why these folders
episodic/for dated observations, meetings, and journals.semantic/<region>/for durable facts and reference knowledge by domain.procedural/for repeatable processes, checklists, runbooks.
This layout is not a hard requirement. It is a practical default that helps both human navigation and memory extraction.
.memora/ sidecar and git
Keep .memora/ out of your notes repository unless you explicitly want derived
state tracked. Typical .gitignore entry:
.memora/
Your markdown remains the durable, user-owned artifact; .memora/ is generated
state for indexing, embeddings, and runtime metadata.
Frontmatter Spec
Memora reads frontmatter for classification, temporal cues, and privacy defaults.
Required fields (recommended baseline)
type:episodic|semantic|proceduraldate: ISO date (YYYY-MM-DD) for primary event/authoring contextregion: short domain label (for semantic grouping)privacy:public|private|secret
Optional fields
tags: array of topical labelsstatus:draft|active|archivedvalid_from,valid_until: explicit claim window hints for extractionsource: external provenance hint (book, meeting, URL, etc.)confidence: self-assessed confidence marker for nuanced material
Full frontmatter example
---
title: drift Serialization Decision
type: semantic
date: 2025-09-12
region: projects/drift
privacy: private
tags: [drift, serialization, architecture]
status: active
valid_from: 2025-09-12
valid_until:
source: leadership-sync
confidence: medium
---
Notes on strictness
Memora can still extract claims from imperfect notes. The frontmatter spec is a quality lever, not a binary gate. Missing optional fields reduce structure but do not block indexing.
Inline Privacy Marker Syntax
Use inline markers when only part of a note is sensitive.
This project update is broadly shareable.
<!--privacy:secret-->
The account number is 4451-XX and pending legal review details are in draft.
<!--/privacy-->
Action items remain public.
Supported marker levels:
publicprivatesecret
How inheritance works
Claim privacy is the strictest of:
- note frontmatter privacy
- inline marker privacy (if present)
So if note privacy is private and inline section is secret, extracted claims
from that section become secret.
world_map.md: What It Is
world_map.md is a generated overview of your current memory topology and review
signals. It is not your canonical writing surface; it is a maintenance and
situation-awareness artifact.
Typical sections include:
- active regions and note density
- recently superseded claims
- stale derivative hotspots
- contradiction clusters
- frontier gaps (thinly connected domains)
- "Today's review" block from the challenger run
When Memora rewrites it
Memora updates world_map.md during indexing/watch cycles when:
- enough new claims enter the graph
- contradiction/staleness landscape changes
- scheduled daily challenger run executes
You can edit this file manually, but treat generated sections as ephemeral.
Reading "Today's review"
The challenger writes concise operational prompts, such as:
- "q4-rollout-plan depends on serialization-strategy-2024, now superseded."
- "drift-bench language disagreement: rust vs go."
- "drift backpressure strategy is still pending decision."
Read this section as a queue for targeted note maintenance, not as an alarm log.
Daily Driver Workflow
This five-step loop is optimized for consistency, not perfection.
1) Capture in Obsidian (or via memora_capture)
Write naturally in Obsidian: meeting outcomes, decisions, reflections, plans.
If you are already in Claude Code and want quick capture, use memora_capture
to append a session note directly to the vault.
Guideline: prefer fast capture over over-structuring. Frontmatter and markers can be refined afterward.
2) Run memora watch in a terminal tab
Keep one terminal running:
memora watch --vault ~/brain
The watch loop handles:
- file change detection
- indexing and extraction
- contradiction checks and supersession updates
- atlas/consolidation updates
- daily challenger pass
This is the background "memory maintenance engine." You keep writing; Memora keeps the claim graph current.
3) Ask questions in Claude Code
Use Claude Code with Memora MCP configured. Ask normal questions:
- "What did the team decide about drift's serialization format?"
- "What changed in deployment strategy this month?"
- "Which assumptions in product strategy were superseded?"
Memora-backed responses include claim citations that are validated before display.
4) Review world_map.md regularly
Open the "Today's review" section every day or two. Act on one or two items:
- resolve contradictions where intent changed
- refresh stale syntheses after major note edits
- fill frontier gaps by capturing missing context
Small, regular interventions are better than monthly cleanup marathons.
Claims vs Prose: What Goes Where
This distinction matters because Memora extracts claims from prose, but not every sentence should be forced into atomic-fact style.
Put in claims (naturally, through prose that contains facts)
- explicit decisions ("We chose X over Y.")
- commitments ("Deadline moved to May 10.")
- stable descriptors ("Service A depends on Service B.")
- measurable observations ("Error rate rose after deployment.")
Leave as prose
- reflections and uncertainty exploration
- narrative context and emotional processing
- rough ideation where assertions are intentionally fluid
- personal meaning-making that is not a stable factual statement
Both are valuable. Reflection-heavy notes may yield fewer extracted claims, and that is expected. The goal is not maximal claim density; the goal is faithful memory with usable evidence.
Practical writing pattern
A useful balance:
- write free-form reflection first
- include a short "Decisions / Facts" section when relevant
- keep sensitive lines wrapped with inline markers when needed
This gives Memora clear extraction anchors while preserving narrative richness.
Privacy Practice: Frontmatter vs Inline Markers
Use the privacy tools intentionally by granularity.
Use frontmatter privacy: secret when
- the whole note is sensitive
- sharing any part externally is inappropriate
- the note is primarily credentials, legal details, or private health data
Use inline markers when
- most of the note is shareable but a few passages are sensitive
- you want one note to hold both operational and confidential details
- you need surgical redaction without splitting files unnaturally
Operational rule of thumb
- whole-note sensitivity -> frontmatter
- partial sensitivity -> inline markers
This keeps notes readable while preserving strict outbound redaction behavior.
Querying Expectations in Practice
When you ask a question, expect three outcomes:
- Verified answer with citations (normal path)
- Answer with fewer citations because some were invalidated and stripped
- Retry-constrained answer generated from verified-only context
If a claim citation cannot be validated against source span fingerprint, it does not pass through as-is. This is central to Memora's trust model.
Maintenance Habits That Keep Quality High
- Keep frontmatter consistent in newly created notes.
- Prefer one fact per sentence for high-signal decisions and commitments.
- Re-index after major refactors or bulk note moves.
- Resolve challenger-highlighted contradiction clusters early.
- Keep vault paths stable when possible; large path churn can increase stale maintenance overhead.
Common Pitfalls
- Over-optimizing templates too early: start with a minimal frontmatter set.
- Ignoring stale flags: stale does not mean wrong, but it does mean review.
- Using
secreteverywhere: overuse reduces model utility; mark surgically. - Treating
world_map.mdas canonical prose: it is generated maintenance output, not your long-form notebook.
Minimal Setup Checklist
- Vault created and initialized with Memora
-
.memora/gitignored - Frontmatter defaults chosen
-
memora watchrunning in one terminal tab - MCP integration configured for Claude Code
- First verified cited query completed
-
First
world_map.mdreview done
Closing
Obsidian remains your writing environment. Memora adds a verifiable memory layer
that tracks claim provenance, temporal validity, privacy boundaries, and citation
integrity. Used daily, the combination gives you both narrative freedom and
evidence-backed recall: write naturally, query confidently, and review memory
health through world_map.md.
Memora Architecture Deep Dive
Catch your AI citing sources that don't say what it claims. Cite, or it didn't happen.
Memora retrieves claims, not notes - atomic facts with source-span pointers, validity windows, and privacy bands. Every LLM citation is architecturally validated against your markdown.
This document describes the concrete system design in four layers:
- Note Graph (vault and markdown substrate)
- Claim Graph (typed factual representation)
- Retrieval and Validation (query-time execution)
- Agent Interfaces (MCP tools and client integration)
The goal is not to maximize retrieval throughput at all costs. The goal is to make unsupported claims difficult to surface and easy to detect.
Layered System Diagram
+--------------------------------------------------------------------------+
| Layer 4: Agent Interfaces (MCP over stdio) |
| memora_query_cited | memora_capture | memora_stale_claims | challenger |
+--------------------------------------------------------------------------+
|
v
+--------------------------------------------------------------------------+
| Layer 3: Retrieval + Validation |
| Hybrid retrieval (BM25 + vector + RRF) |
| Spreading activation over links/edges |
| Privacy filter + typed prompt builder |
| Citation validator (span re-read + fingerprint recompute + retry) |
+--------------------------------------------------------------------------+
|
v
+--------------------------------------------------------------------------+
| Layer 2: Claim Graph (SQLite + edge model) |
| claim(subject,predicate,object,span,fingerprint,time,privacy,status) |
| edges: entails | contradicts | supersedes | derives | co_occurs |
| provenance DAG + stale propagation |
+--------------------------------------------------------------------------+
|
v
+--------------------------------------------------------------------------+
| Layer 1: Note Graph (Obsidian vault + watcher) |
| markdown notes + frontmatter + inline privacy markers |
| file events -> parse -> extraction jobs |
+--------------------------------------------------------------------------+
Design Principles
Memora uses a narrow set of invariants:
- Claims are addressable units. All downstream reasoning references claim ids, not free text snippets.
- Source spans are mandatory. Every extracted claim stores a byte range into a specific markdown file.
- Fingerprints are mandatory. A blake3 hash over the exact source span is captured at extraction and checked at validation time.
- Validity is explicit. Claims can become historical (
valid_until) without disappearing from history. - Privacy is monotonic. A claim can only move toward stricter visibility when inherited or overridden.
These rules are enforced in the data model and request pipeline, not only by prompts or post-hoc heuristics.
Layer 1: Note Graph
The Note Graph is the user-owned vault. Memora does not replace it and does not rewrite user prose as a primary persistence strategy. Notes remain markdown files editable in Obsidian or any text editor.
1.1 Vault as source of truth
Memora treats the vault as canonical for natural language content. Derived state
is stored in .memora/ and the local database; the authoritative prose remains
in markdown.
Input channels into this layer:
- manual note edits in Obsidian
- captured notes from MCP tools (for example
memora_capture) - file-level moves, renames, and deletions
1.2 Structural parsing
Each note is parsed for:
- frontmatter fields (type, date, region, privacy, tags, status, etc.)
- body sections
- inline privacy markers:
<!--privacy:secret--> ... <!--/privacy-->
The parser outputs typed segments with byte offsets. These byte offsets are the backbone for source-span references in claims.
1.3 Incremental change detection
A watcher detects modified files and schedules extraction jobs. The queue keeps work bounded and avoids full re-indexing for every keystroke. Hash-based change detection ensures unchanged notes are skipped.
1.4 Why this layer matters
Most memory systems jump directly from note chunks to retrieval. Memora adds a formal extraction boundary so claims can carry strict provenance. This boundary is what makes downstream citation checks deterministic.
Layer 2: Claim Graph
The Claim Graph is the core memory model. A claim has typed fields and graph relations to other claims.
2.1 Claim schema
Core fields:
subject,predicate,objectsource_notesource_span_start,source_span_end(byte offsets)span_fingerprint(blake3)valid_from,valid_untilprivacy_band(public,private,secret)- lifecycle flags (
current,superseded,stale, etc.)
This schema separates statement identity from rendering text. The same concept can be tracked across time with explicit supersession edges rather than silent overwrite.
2.2 Edge model
Memora stores directional, typed edges:
entailscontradictssupersedesderivesco_occurs
Edges support both retrieval-time context expansion and maintenance-time staleness propagation.
2.3 Storage strategy
Primary persistence is SQLite:
- relational tables for claims and edges
- FTS5 index for lexical retrieval
- JSON1-compatible metadata fields for structured payloads
Embeddings are stored for semantic retrieval and indexed with HNSW. This allows hybrid retrieval while keeping the memory model local-first.
2.4 Provenance and stale propagation
Synthesis claims store provenance links to source claims (derives). If any
source becomes invalid or superseded, dependent syntheses are marked stale.
This stale bit is not an error state. It is a review signal: the synthesis might still be useful, but it should be revalidated against current inputs.
Layer 3: Retrieval and Validation
This layer is where query-time guarantees are enforced.
3.1 Retrieval cascade
Retrieval is not a single query operation. It is a staged cascade:
- lexical retrieval from FTS5 (BM25-style scoring)
- vector retrieval from HNSW embeddings
- reciprocal-rank fusion (RRF)
- privacy filtering
- prompt context assembly
The output is a ranked claim set, not note paragraphs.
3.2 Citation validator
The validator checks every claim id cited by the model:
- fetch claim row by id
- open source markdown note
- read exact byte span
- recompute blake3 fingerprint
- compare to stored fingerprint
If any citation fails (missing id, span mismatch, fingerprint mismatch), that citation is rejected. Depending on tool mode, rejected citations are stripped and the model is retried using verified-only context.
3.3 Retry semantics
Retry is scoped and deterministic:
- first pass: full retrieved context
- validation pass: verify cited ids and spans
- fallback pass: re-prompt with only validated claim context
This avoids silently passing unverifiable citations through to the user.
3.4 Temporal and contradiction awareness
Retrieval defaults to currently valid claims. Historical queries can include time filters to reconstruct prior states without flattening history.
Contradictions are represented explicitly and alter validity windows rather than deleting data. That keeps historical trails auditable.
Layer 4: Agent Interfaces
Memora exposes capabilities through MCP over stdio. Clients query tools instead of linking to private internals.
4.1 Tool surface
Representative interfaces include:
- cited query tool for validated responses
- capture tools for episodic logging
- contradiction and stale-claim inspection tools
- challenger outputs for periodic review
Tool responses are shaped around claim ids and provenance so clients can show evidence, not just summaries.
4.2 Why MCP boundary matters
The interface layer keeps guarantees portable across clients. A user can switch between environments without changing citation behavior because validation lives inside Memora, not in client-specific prompt templates.
4.3 Failure mode handling
If external model output includes invalid citations, Memora handles that inside the pipeline. The client receives either verified results or explicit failure signals, not a hidden downgrade.
Claim Lifecycle Walk-through
This is a concrete path for one statement from note to cited answer.
- User writes text in
semantic/projects/drift/roadmap.md: "drift switched serialization from JSON to MessagePack after throughput benchmarks." - Watcher detects file change and schedules extraction.
- Extractor emits claim candidate:
drift | uses_serialization | messagepack. - Source span recorded as byte offsets into the markdown file.
- Fingerprint computed over that exact span and stored in claim row.
- Claim indexed in FTS and embedding index; edges updated as needed.
- User asks query through an MCP client.
- Retrieval returns ranked claim set including this claim id.
- Model drafts answer and cites
[claim:abc123]. - Validator re-opens source note, slices stored byte range, recomputes blake3, and confirms it matches stored fingerprint.
- Citation marked verified and answer is returned.
- User sees output with verified citations that map back to source text.
If step 10 fails, citation is removed and the retry path runs on verified-only claims.
Contradiction Handling Walk-through
Contradictions are a first-class maintenance event.
- A new claim arrives:
drift-bench | uses_language | go. - System fetches candidate claims with matching subject/predicate and current validity windows.
- Contradiction judge evaluates pairs (
new,candidate) for conflict, such asgovsrust. - If conflict is confirmed:
- older claim receives
valid_until = new.valid_from(or event timestamp) supersedesedge is written (new -> old)contradictsedge is written for explainability
- older claim receives
- Derivative claims linked to the older claim are marked stale.
- Challenger later surfaces stale derivatives for review/regeneration.
Two properties are intentional:
- old data is retained for historical reasoning
- current reasoning can exclude superseded claims by default
Privacy Redaction Walk-through
Privacy is applied during extraction and enforced before prompt construction.
- Parser reads note-level frontmatter privacy.
- Parser reads inline privacy markers in body spans.
- Each extracted claim receives
privacy = max(note_level, inline_level). - Cloud LLM extraction prompts redact secret inline spans (length-preserving) and skip wholly secret notes.
- Query pipeline applies privacy filter before prompt assembly.
- Prompt builder accepts typed claim wrappers that distinguish:
- local-safe claims
- redacted claims for cloud transport
- For cloud model calls, secret claims are transformed:
subjectpreservedpredicate,objectreplaced with[redacted]
- MCP
memora_get_noteredacts secret note bodies at the read boundary. - Non-redacted secret claims are rejected by type constraints and cannot enter outbound model payload construction.
This architecture turns privacy from a policy suggestion into a compile-time and runtime boundary.
Five Differentiators
1) Architectural citation enforcement
Most systems ask the model to cite correctly and then trust it. Memora stores source spans and fingerprints at extraction time, then verifies cited ids by re-reading markdown at response time. This allows deterministic rejection of invalid citations and a retry path constrained to verified context.
2) Claim-first memory model
The memory primitive is an atomic claim, not a note chunk. Claims carry temporal state, privacy, provenance, and edges. That enables operations like supersession, stale propagation, and relation-aware retrieval that are difficult to represent when the only unit is free text.
3) Time-aware contradiction handling
Contradictions are modeled as graph relations with validity window updates. New claims can supersede old ones without deleting history. Queries can target either the current world state or historical states, and stale derivatives can be identified explicitly.
4) Per-claim privacy with typed redaction
Privacy is resolved at claim level using note metadata plus inline marker overrides. Secret claims can still contribute structurally while restricting outbound content. Cloud-bound payloads enforce redaction before model transport, which is stronger than relying on operators to remember manual filtering.
5) Active challenger loop
Memora runs a periodic challenger that inspects stale dependencies, contradictions, and sparsely connected frontier areas. This is not just retrieval: it is maintenance of memory integrity over time. The result is a continuously reviewable world map instead of a static index.
What We Do Not Do
- Not a faster RAG benchmark story: Memora's primary claim is architectural verifiability. Benchmark speed/latency comparisons against other systems are not published here.
- Not a multimodal memory store yet: current substrate is markdown-first with structured metadata; rich multimodal ingestion is future work.
- Not federated multi-vault reasoning yet: primary target is one personal vault and its sidecar state.
- Not a research-agent framework: Memora is a memory engine and interface layer, not an autonomous planner for open-ended web research.
Operational Notes
- Local-first operation works offline with local model backends.
- Derived state is reproducible from vault content plus config.
- Citation verification depends on byte-accurate spans; manual file rewrites are expected and handled through re-indexing.
- Claims may be skipped when extraction confidence or structure is insufficient; absence of a claim does not imply absence of prose.
Closing
Memora is engineered around a strict contract: if an answer cites your notes, the citation must survive structural checks against the source text. The system is intentionally conservative where trust boundaries are involved, and that conservatism is what enables personal-memory workflows where incorrect citations are treated as system errors, not normal behavior.
Citation Protocol
Memora treats citations as verifiable references to claim IDs.
Claim marker format
LLM responses use [claim:<id>].
Example:
drift switched to MessagePack for serialization [claim:drf75a1c9e10b2aa]
Verification steps
- Parse claim markers from LLM output.
- Resolve each claim ID from SQLite.
- Resolve note path and byte span.
- Re-read source span from markdown body.
- Recompute full-width BLAKE3 (256-bit) fingerprint and compare. Legacy 64-bit fingerprints written by older versions still verify until the vault is re-indexed.
- Optionally verify quote overlap (substring containment — provenance, not entailment).
- Check temporal validity: a claim whose
valid_untilhas passed issuperseded.
Status values
verifiedunverified(claim missing / hallucinated id)fingerprint_mismatch(source changed since extraction)quote_mismatch(marker quote not contained in the span)superseded(provenance intact, but the claim'svalid_untilhas expired — surfaced, not asserted as current)
Verified answer semantics
clean_text is rewritten to keep only statements supported by verified claim markers.
This means "verified" in Memora is an architectural property (data + hash + span), not a prompt instruction.
For example, a verified claim can point to semantic/projects/drift/roadmap.md with a span that captures the MessagePack decision text.
How Memora compares
Catch your AI citing sources that don't say what it claims.
This page is deliberately honest about where Memora leads and where it is behind. It is an architectural comparison, not a published head-to-head benchmark.
The one claim no funded vendor makes
Memora performs post-generation, hash-reverified citation rejection: after the model answers, Memora re-reads the verbatim source span behind each cited claim, recomputes its blake3 fingerprint, and strips any citation the source does not actually contain before the answer is returned. The contract is enforced by Rust types and span hashes, not by prompting.
No funded agent-memory vendor does this. Mem0, Zep, Letta, and Cognee retrieve and store memory well, but they do not re-read the source and reject citations. Anthropic's Citations API returns offsets the model asserts — it does not independently re-read and re-hash them.
| Memora | Mem0 / Zep / Letta / Cognee | Anthropic Citations API | |
|---|---|---|---|
| Hash-reverified citation rejection | Yes | No (no re-read / rejection) | Model-asserted, not re-hashed |
| Entailment — does the quote support the claim | Optional, LLM-judged (opt-in), kept separate from the proof | Partial (LLM-judged) | No |
| Temporal validity windows | Yes | Yes (Zep/Graphiti) | — |
| Contradiction / supersession | Yes | Yes (Zep/Graphiti) | — |
| Per-claim privacy redaction at the wire | Yes | Varies | — |
| Local-first, single binary, no service | Yes | Mostly hosted/service | Cloud API |
| Scale, integrations breadth, multimodal | Behind | Ahead | — |
| Hosted offering, ecosystem, traction | Behind | Ahead | Ahead |
Two of these rows matter more than the rest, and they are the honest ones: the "provenance only, by design" row (we tell you exactly where our guarantee ends) and the "behind on scale/ecosystem" row (we are not pretending to out-scale a funded team). Those two admissions are what make the top row believable.
What Memora guarantees — and what it does not
- Provenance integrity (guaranteed): the cited source span verbatim exists, is unmodified (hash-proven), and contains any quoted text. This is strictly stronger than model-asserted citations.
- Entailment (optional, not guaranteed): whether the source actually supports
the assertion (rather than just containing the quote) is a separate, harder
question.
memora verify --entailmentruns an opt-in, LLM-judged check and flags citations the source does not support. It is best-effort, kept clearly separate from the hash-proven provenance above, and never sendssecretcontent to a cloud model.
The landscape, by category
For each system the question is narrow: when an LLM answer must be grounded in your content, how is the citation verified?
Agent-memory layers — Mem0, Zep / Graphiti, Letta, Cognee
The category leaders. Strong on storing, recalling, and (Zep/Graphiti) temporally modeling memory across sessions, with broad framework integrations and hosted offerings. Citations, where present, are model- or prompt-level — the system does not re-read the original source span and reject a citation whose bytes don't match. Memora is behind these on scale, integrations, and ecosystem, and overlaps them on temporal validity and contradiction handling (so Memora does not market those as unique). Memora's divergence is the hash-reverified rejection step and per-claim privacy redaction at the wire boundary.
Grounded-citation APIs — Anthropic Citations
Returns source locations the model asserts for a generated answer. Useful and first-party, but the offsets are not independently re-read and re-hashed against the source after generation. Memora's divergence: it treats the model's citation as a claim to be re-proven, not as ground truth — and rejects it on a hash mismatch.
PKM-AI over notes — Obsidian Copilot, Smart Connections, Khoj, Notion AI
Excellent zero-config retrieval and chat over personal notes, often free and local. Citations are typically prompt-level: the model is asked to reference notes, with no strict post-generation verification of each cited identifier against an immutable span. Memora is slower and higher-friction here (it indexes claims and needs an LLM for extraction), and diverges by enforcing the citation contract structurally rather than trusting the model to quote faithfully.
Traditional RAG over Obsidian / markdown
Index chunks, retrieve top-k, pass to the model with citation guidance in the prompt. Temporal reasoning is usually recency-biased; privacy is folder/note-level; synthesis staleness is rarely tracked. Memora diverges by retrieving atomic claims with byte-range provenance, verifying every cited id against its source span, modeling validity windows and supersession, and tracking stale derived claims over a provenance DAG.
Summary matrix (architectural)
| Dimension | Traditional RAG | PKM-AI (Copilot/Khoj/…) | Agent memory (Mem0/Zep/Letta) | Anthropic Citations | Memora |
|---|---|---|---|---|---|
| Atomic unit | chunks | notes/snippets | memories/graph nodes | model spans | atomic claims |
| Citation model | prompt-level | prompt-level | prompt/model-level | model-asserted offsets | hash-reverified rejection |
| Temporal model | recency | manual | recency / bi-temporal (Zep) | — | validity windows + supersession |
| Privacy model | folder/note | folder/note | varies | — | per-claim + typed wire redaction |
| Staleness tracking | usually none | usually none | varies | — | provenance DAG + stale flags |
| Deployment | library | app/plugin | mostly hosted | cloud API | local single binary |
Final note on benchmarking
This is an architectural comparison. Memora has not been run in published
head-to-head retrieval benchmarks against these systems, and this page makes no such
numeric claims. The one number Memora does publish — its citation-rejection rate over
a labeled fixture — is reproducible locally with make bench and is described in the
README. Vendor
capabilities evolve; verify current feature sets against each vendor's own docs.
MCP Tools
All tools return JSON payloads.
Memora MCP reads configuration from {vault}/.memora/config.toml (embedder,
retrieval top_k, privacy, and LLM provider settings), matching the CLI.
Environment variables
| Variable | Required | Purpose |
|---|---|---|
MEMORA_VAULT | yes | Path to your Obsidian-compatible vault |
MEMORA_INDEX_DB | optional | Override index DB path (default: {vault}/.memora/memora.db) |
MEMORA_VECTOR_INDEX | optional | Override vector index path (default: {vault}/.memora/vectors) |
MEMORA_ENABLE_NETWORK_LLM | for LLM tools | Set to 1 to enable network LLM calls |
When MEMORA_ENABLE_NETWORK_LLM is unset, memora_query_cited still works using
extractive verified fallback (indexed claims only, degraded: true).
memora_consolidate and memora_challenge require network LLM and return an
error if it is disabled.
Example Claude Desktop config:
{
"mcpServers": {
"memora": {
"command": "/absolute/path/to/memora-mcp",
"env": {
"MEMORA_VAULT": "/absolute/path/to/your-vault",
"MEMORA_ENABLE_NETWORK_LLM": "1"
}
}
}
}
memora_query
Input:
{"query":"What did the team decide about drift's serialization format?","k":5}
Output:
{"hits":[{"id":"note-1","summary":"drift moved to MessagePack in Q3","region":"projects/drift","score":0.12,"snippet":"..."}],"regions_used":["projects/drift"]}
Snippets redact secret note bodies and inline <!--privacy:secret--> regions.
memora_query_cited
Input:
{"query":"What did the team decide about drift's serialization format?","k":5}
Output:
{
"clean_text": "drift switched from JSON to MessagePack [claim:drf75a1c9e10b2aa]",
"verified_count": 1,
"degraded": false,
"checks": [{"claim_id": "drf75a1c9e10b2aa", "status": "verified"}]
}
When network LLM is disabled, responses are built extractively from indexed
claims with degraded: true. Secret claims sent to cloud providers are redacted
per [privacy].redact_secret_in_cloud in vault config.
memora_get_note
Input: {"id":"note-1"}
Output keys: id, region, summary, privacy, body, body_redacted,
tags, refs, wikilinks
Secret notes return "body": "[redacted]" and "body_redacted": true.
memora_get_atlas
Input: {"region":"projects/drift"}
Output keys: region, atlas_markdown, note_count
memora_get_world_map
Input: {}
Output: {"markdown":"# World Map ..."}
memora_capture
Input:
{"region":"inbox","summary":"Quick capture","body":"...", "tags":["inbox"], "privacy":"private"}
Output: {"id":"note-...","path":"inbox/note-....md"}
Region paths must stay inside the vault (.. and absolute paths are rejected).
Captured notes are indexed with claim extraction when network LLM is enabled.
memora_consolidate
Input: {"scope":"all"} or {"scope":"region:work/projects"}
Output keys: regions_rebuilt, notes_moved
Requires MEMORA_ENABLE_NETWORK_LLM=1.
memora_verify_claim
Input: {"claim_id":"abcd1234"}
Output keys: exists, span_intact, current_text
memora_stale_claims
Input: {}
Output: array of stale claim rows.
memora_contradictions
Input: {"subject":"drift-bench"} (optional)
Output: array of contradiction rows.
memora_challenge
Input: {}
Output: ChallengerReport JSON.
Requires MEMORA_ENABLE_NETWORK_LLM=1.
memora_decisions
Input: {}
Output: [{id,title,decided_on,status}]
License
Apache 2.0 only. See LICENSE.
Why Apache 2.0
Personal memory infrastructure should be portable, forkable, and embeddable without surprise. Apache 2.0 was chosen specifically because:
-
It's permissive without being a free-for-all. Companies and individuals can build on Memora - wrappers, internal forks, MCP integrations, distribution bundles - without infecting their own license. That matters for adoption inside organizations where copyleft would be a non-starter.
-
It includes an explicit patent grant. Contributors who hold patents cannot retroactively assert them against downstream users of the code they contributed to. For a tool whose differentiators (claim graph, span fingerprint, citation validator) sit close to active patent thickets in the LLM tooling space, the explicit grant is non-negotiable.
-
It survives M&A. A future Memora acquirer cannot revoke the license on existing releases. Once Apache 2.0 is published, it stays Apache 2.0 for that version, forever.
-
It's compatible with the rest of the Rust ecosystem. Most Cargo crates are MIT or Apache 2.0 dual-licensed; choosing Apache 2.0 lets us depend on them without friction and lets others depend on us symmetrically.
What Apache 2.0 means in practice
You can:
- Use Memora commercially.
- Modify Memora and distribute the modifications (under Apache 2.0).
- Patent your own improvements while keeping Memora's grant intact.
- Bundle Memora into a closed-source product, as long as you preserve the notice file and don't claim Memora endorsement.
- Run Memora on private vaults, internal infrastructure, or air-gapped systems without contacting anyone.
You must:
- Include a copy of the Apache 2.0 license with any redistribution.
- Preserve copyright, patent, trademark, and attribution notices.
- State significant changes if you redistribute modified versions.
- Not use the Memora name or logo to imply endorsement of a fork without permission.
SPDX identifier
SPDX-License-Identifier: Apache-2.0
Full text
See LICENSE in the repository root for the canonical Apache 2.0 text.