One memory.
Many intelligences.
Chitraq is a persistent computational memory. A deterministic core owns identity, versioning, provenance and truth; models work over it and are never allowed to own it. Swap the model, keep the memory — including everything the last one contributed, still labelled as its work.
The idea
Most tools here are storage with search bolted on. Storage answers "where is the file?" Memory has to answer "what do I know, how do I know it, what has changed, and what matters now?"
Knowledge, not documents
A file is kept verbatim as a Source. The claims, decisions and observations inside it become separate objects, each with its own status, evidence and history — so one paragraph can be superseded without rewriting the document it came from.
Provenance never gets lost
Every object records whether you wrote it, a source stated it, an algorithm derived it, or a model produced it. Confirming an AI-derived fact marks it confirmed; it does not relabel it as yours.
History is never rewritten
Editing appends a version. Superseding keeps the old object readable
and linked forward. "What did we believe in March?" is a question with an
answer. Only an explicit erase, with a stated reason,
destroys anything.
Intelligence proposes; you decide
No model writes to memory. A capability returns a value, the value becomes a proposal, and a proposal becomes knowledge only when accepted. There is no second path in — it is enforced in one place and tested.
It answers before it spends
Most questions to a memory are lookups, and a lookup does not need a language model — it needs the sentence you already wrote. Chitraq tries that first, and escalates only when quoting genuinely cannot answer: when nothing matched, when the match is too weak to trust, or when the question asks for synthesis.
| Outcome | of 10 |
|---|---|
| answered by quoting your own words | 6 |
| served from cache | 2 |
| escalated to a model | 2 |
Measured over ten realistic questions. 80% never reached a model. A test counts model calls directly, so a change that quietly starts sending everything to a paid provider fails the suite.
$ chitraq ask "why did we drop the redis cache"
quoted from your own words confidence 0.82
The hit rate never exceeded 30% and the
memory cost was not worth it.
from obj_01J8F… · user · confirmed
cost nothing — no model was called
took 94ms
The cache is keyed on the content hashes of the objects an answer rests on, so editing any of them moves the key. A stale answer cannot be served, because if it could be stale the key already changed.
Get started
# Nothing to install. Node ships SQLite.
git clone https://github.com/Nulfied/Chitraq
cd Chitraq
npm test
# Point it at notes you already have
chitraq ingest ~/Documents/notes
# Then ask
chitraq ask "what did we decide about pricing"
Requires Node 24. There are no dependencies to install —
package.json has an empty dependencies block and
it is meant to stay that way.
Optional: make it smarter, still free
ollama pull nomic-embed-text # semantic search
ollama pull moondream # read images
ollama pull llama3.2 # written answers
Everything runs on your machine at no cost. Without Ollama the built-in deterministic provider still serves every capability — it is the floor the whole system stands on, and the test suite passes with no models installed at all.
Embeddings are the biggest single win: head to head on queries sharing almost no words with the notes, the built-in embedder found 1 of 3 and Ollama found 3 of 3.
One memory, several programs
$ chitraq tokens --new formfit --scope write
import { ChitraqClient } from 'chitraq/client'
const memory = new ChitraqClient({
url: 'http://127.0.0.1:4317',
token: process.env.CHITRAQ_TOKEN,
})
await memory.remember({
title: 'Compress PDFs in the browser',
kind: 'decision',
})
const answer = await memory.ask(
'why do we compress client-side'
)
The client is one file with no dependencies. It never caches and never retries blindly — a memory client that quietly returns a stale answer leaves you unable to tell a remembered fact from a remembered response.
Presenting a token constrains you. Presenting nothing changes nothing.
On a loopback install a token cannot make access harder to obtain —
anything on the machine could call the API anyway. What it does is make
access narrower. A read token is refused a write
even where an anonymous caller is allowed one, so a side
project can hold a credential that genuinely cannot damage the memory
it reads.
| Scope | May |
|---|---|
read | search, ask, recall, entities |
write | read, plus capture and review |
admin | everything, including erase |
Measured, not assumed
Two numbers in this codebase were guesses, and both were wrong by orders of
magnitude until something measured them. The approximate-index threshold was
set at 20,000 vectors; measurement put it at 5,000. A code comment claimed
brute-force search was "sub-millisecond at 10k chunks"; it was 152 ms at
12k. scripts/bench-vectors.js exists so the next guess gets
caught too.
Vector search
| Vectors | Exact | Approx | Recall |
|---|---|---|---|
| 400 | 4.2 ms | 0.55 ms | 1.00 |
| 2,000 | 21 ms | 1.1 ms | 1.00 |
| 6,000 | 56 ms | 2.3 ms | 1.00 |
| 12,000 | 152 ms | 12.2 ms | 1.00 |
On a laptop with no GPU
| Operation | Time |
|---|---|
| quoted answer — the common path | 70–320 ms |
| embedding a query | ~190 ms |
| reading an image (moondream) | 3.7 s warm |
| generated answer (llama3.2) | 41–50 s |
That last row changed a design decision. Escalation uses measured latency, not what an adapter declares — Ollama claims 3 s and takes 45 here. Above the threshold Chitraq answers instantly from your own words and offers the slow one, with the real wait on the button.
What it refuses to do
Guess silently
An image nothing can read is stored verbatim with a note naming the capability that would unlock it. When a model does read it, the source permanently records that its text was read rather than written — a misread "38ms" is indistinguishable from a quote otherwise.
Pick a winner
Where two machines edited the same thing, both are kept and the disagreement is raised. Last-writer-wins destroys one of two real edits with nobody the wiser.
Take a dependency
PDF text extraction, scanned-page images, vector search, hashing and the HTTP server are all written against what Node already ships. Every dependency is a thing that can break, change licence, or be taken over.
And it says where it is weak. Fax-encoded scans cannot be read. Concepts are the shallowest entity kind. The Claude adapter is written and has never run against the live API. Nobody has used this daily for a month. The full list lives in STATUS and is kept current rather than trimmed.
Read further
Status
What is implemented, what is partial, what is deliberately not built — with an honest weaknesses section and every measurement on this page.
Invariants
The sixty rules the code is built to hold, each naming where it is enforced and the test that proves it.
Architecture
How capture, retrieval, context and the intelligence fabric fit together, and why the boundaries fall where they do.