chamber
Chamber
Ask questions about your own notes. Get answers that cite their sources — and a daily check that tells you when a source has changed underneath a conclusion you already trusted.
Zero runtime dependencies. Everything is node:sqlite and files on your disk.
No account, no cloud call unless you point it at one.
See it in two minutes
Requires Node 23.6+ — Chamber runs TypeScript directly, with no build step.
git clone <this repo> chamber && cd chamber
npm ci && node --experimental-strip-types src/cli.ts tryNo config, no database, no model, no network. It builds a throwaway workspace,
runs the real code paths against it, and deletes it (--keep to look around).

That recording is scripted from assets/demo.tape rather
than hand-captured, so it is regenerated when the output changes instead of
quietly showing a version of Chamber that no longer exists. Everything below is
the same command's actual output, trimmed:
$ chamber believe belief "Customers may return any purchase within 30 days of delivery."
committed blf_ddcf4f3c9b2e81b8
an unsourced assertion is not refused — it mints citation debt.
$ chamber debts
dbt_18bd1c1171cdbfcb [pending]
$ chamber pay-debt
proposed 2 source(s), 2 pinned; best=0.694
$ chamber verify
blf_ddcf4f3c9b2e81b8 2/2 pins verifiedThat is the ordinary state: a belief standing on evidence that still holds. Then
someone edits the note it was built on — 30 days becomes 14 days:
$ chamber ingest ./notes
ingested 2 file(s) as 4 passage(s)
$ chamber verify
blf_ddcf4f3c9b2e81b8 1/2 pins verified
hash_mismatch: refunds.md#p0Nobody asked it to re-examine that belief. The conclusion did not change; the ground under it did, and the exit code is non-zero, so a scheduled job can act on it. That is the whole product.
Four more scenarios — a rolled-back ledger caught by an outside anchor, a
sandbox that refuses rather than degrade, a hostile tool catalogue rejected —
are in demos/, and run in CI so they cannot drift from the code.
Related MCP server: brainMD
A dictionary for the words above
Everything Chamber does is rows in one SQLite file. Each term in the transcripts names a table or a hash:
Word | What it actually is |
passage | one chunk of one markdown file. |
belief | a row in |
pin | a sha-256 of a cited passage's stored title, body and ref, taken at the moment of citation and kept in |
verify | re-read every pinned passage, recompute the hash, compare. Any mismatch exits non-zero. No model involved. |
citation debt | a row in |
pay-debt | retrieval proposes passages for the indebted claim; accepting them pins them. |
APORIA | the verdict when no retrieved passage supports an answer. The reply is "I don't know", recorded as that. |
gate | a check and a write inside one SQLite transaction — both commit or neither does. |
audit log | append-only |
anchor | the log's root hash stored outside the database, so truncating the log is detectable rather than silent. |
the scheduler | a launchd/systemd job running |
None of it is hidden machinery: sqlite3 ~/.local/share/chamber/chamber.sqlite '.tables' shows the whole thing.
Answers that cite their sources
With a model configured, chamber ask judges every sentence on its own
citations. Against the same two sample notes, on a local 30B:
$ chamber ask "summarise our refund policy"
Customers may return any purchase within 30 days of delivery [2]. Refunds
are issued to the original payment method, usually within five working days
of the returned item arriving at the warehouse [2]. However, perishable goods
and personalised items cannot be returned once dispatched [1].
[ALLOWED] Customers may return any purchase within 30 days of delivery [2]. Refu
sources: refunds.md#p0 — refunds › Refund policy, refunds.md#p1 — refunds › Refund policy › ExceptionsThe model is shown [1]…[k] and never a document id or a hash, so it cannot
fabricate a citation even in principle — the numbers are resolved back to files
after the answer is written. A sentence that cites nothing is marked
UNSUPPORTED: recorded, but not treated as load-bearing.
Asking something the corpus cannot answer is the more important case:
$ chamber ask "what should a customer do if they want to return a perishable
item after the office has closed?"
I don't know
[APORIA] I don't knowBoth notes are in the index and both are relevant. Neither answers the question, so nothing is composed from the pieces.
Pointing it at your own notes
npm link # puts `chamber` on your PATH
chamber init # writes ~/.config/chamber/config.jsonThen edit that config to add a notes folder and a model:
{
"database": "~/.local/share/chamber/chamber.sqlite",
"model": { "base": "http://127.0.0.1:8087/v1", "name": "your-model", "mode": "openai" },
"ingest": [{ "root": "~/Notes", "exclude": ["transcripts", "attachments"] }]
}model.base may name any OpenAI-compatible endpoint. A loopback address needs
no API key; anything else reads CHAMBER_API_KEY from the environment, never
from the file.
chamber ingest # index every configured root
chamber ask "..." # ask, with citations
chamber verify # re-check stored pins against the corpus
chamber corpus # what is actually in the indexSet your excludes before the first ingest. There is no default exclude list.
Pointed at a folder of exported chat logs, Chamber will happily index all of
them and answer from them — see chamber corpus and
docs/KNOWN_LIMITATIONS.md entry 11.
Run it daily
deploy/launchd/com.chamber.verify.plist (macOS) and deploy/systemd/
(Linux) run ingest and verify on a schedule, and raise a notification only when
something drifted. A check that correctly reports nothing on most days is a
check you stop reading, so it stays quiet until it isn't.
Use it from an AI coding agent
src/mcp_server.ts exposes three tools over MCP — chamber_ask,
chamber_verify, chamber_corpus — so a host like Claude Code can query your
corpus and see the per-claim citation verdicts rather than just the prose.
claude mcp add -s user chamber \
-e CHAMBER_PYTHON=/path/to/python-with-onnxruntime \
-- /absolute/path/to/node --experimental-strip-types /path/to/chamber/src/mcp_server.tsBoth absolute paths are deliberate. A spawned MCP server does not inherit your
interactive shell's PATH: node may resolve to a version below the 23.6
floor, and python3 to one without onnxruntime — which makes the embedder
fall back to non-semantic hash vectors and every question answer "nothing in
the corpus matches." Naming the interpreters is the only reliable fix. See
docs/KNOWN_LIMITATIONS.md entry 15.
The server resolves config once, on its first tool call, and pins it for the
life of the process — so reconnect the server after editing config. Editing
model.base while a host held the process open produced ECONNREFUSED against
the old address while the CLI answered fine from the same file, which reads
as a broken config rather than a stale daemon. The resolved database, mode and
base are printed to stderr on first use so the host's MCP log can settle it.
Nothing on that surface can activate a skill, approve a pending write, or ingest — the gates exist so a human passes through them, and handing a model the approval side would invert them rather than weaken them.
chamber_ask is not read-only, and the write is not just bookkeeping: every
claim goes through the commit gate, so a claim with verified citations is
recorded as a belief with its pins — which is exactly what chamber verify
later re-checks for drift. Unsourced assertions mint citation debt; spend is
recorded. This is the same behaviour as chamber ask on the command line. The
guarantee is that the gate is not bypassed, not that nothing is written.
What a verified citation does and does not prove
Chamber proves a cited passage is the passage it claims to be — unmodified, still present, still saying what the citation says it says.
It cannot tell you the claim follows from the passage. A model can cite a real source and misread it, and every layer here will pass it. That is a stated non-goal, it has been observed happening, and it is not solved.
Read docs/KNOWN_LIMITATIONS.md before trusting
any output. Seventeen limitations are documented there, including the two least
flattering. The sandbox confines only where bubblewrap works — Linux with
unprivileged user namespaces — and refuses to run anything anywhere else, which
is safe but is not the same as working. And citation debt blocks a verbatim
repeat reliably, while the paraphrase leg over it is a heuristic: calibration
found no cosine threshold that separates a restatement from a contradiction. A
numeric and negation check now removes the worst of that — an operator
correcting an indebted claim is no longer refused for restating it — but two of
five true paraphrases still slip through, and a contradiction that is neither
numeric nor negated still reads as a repeat.
The invariant
No assertion may become executable, citable, or load-bearing except through a gate whose check and write commit in one transaction — anything else may decay, park, or be defeated, but it may never silently pass.
Gate | Blocks when |
| assertion with open blocking citation debt; missing or unverifiable pins; a defeater used as a source; a belief-typed commit on the fast path |
| open holds; load-bearing stale beliefs; content ≠ last critic-cleared hash; capability manifest over-ask |
Both gates write into a hash-chained audit log — entry_hash = sha256(prev_hash || canonical JSON) with an incremental Merkle tree — so altering a past
decision breaks every hash after it. Retraction types (defeater, unknown)
commit freely and never mint blocking debt.
Defaults are refusals: memory and skill writes require approval, learned skills land in quarantine rather than applying silently, and a pending write that expires is not an approved one.
Development
npm test # 308 tests
npm run typecheck
npm run probes # adversarial probes; each one asserts a defect is absentnpm run probes is expected to fail today. Two probes report real, open
defects — sandbox_escape and debt_paraphrase — and they are wired in
deliberately. A gate that cannot fail reports safety it never checked.
Layout
src/ask.ts retrieval → prompt → per-claim citation gate
src/mcp_server.ts the read side over MCP: ask, verify, corpus
src/commit_belief.ts the belief gate; check and write in one transaction
src/pins.ts content pins and drift verification
src/audit.ts hash-chained log + incremental Merkle
src/config.ts settings: flag → env → config file → default
src/db.ts opens the database, loads every schema
probes/ adversarial probes, run by npm run probes
demos/ the four scenarios above, run in CI so they cannot rot
docs/KNOWN_LIMITATIONS.md what does not work, and what it costsMIT.
This server cannot be installed
Maintenance
Related MCP Servers
- Alicense-qualityAmaintenanceA local-first CLI and MCP server that helps you build and search a personal knowledge vault from Markdown notes, with semantic search and AI-powered features like stale note detection and session memory harvesting. It’s provider-agnostic, requires no GPU in its default mode, and exposes your vault as long-term memory to any MCP-compatible AI tool like Claude Code.45Apache 2.0
- Alicense-qualityAmaintenanceLocal-first markdown vault with a built-in MCP server (streamable HTTP). 16 tools and 2 resources for Claude Code / Desktop / Cursor: read/write/search plus context_for_query, find_orphans, weekly_digest, compare_notes, semantic_outline. Per-folder agent permissions, LanceDB vectors, local Xenova ONNX embedder swappable to Ollama. Single Bun binary. AGPL.26AGPL 3.0
- AlicenseAqualityAmaintenanceLocal-first agentic knowledge layer over Obsidian notes, enabling MCP-aware agents to search, retrieve, and compile knowledge with provenance and task contracts.37360MIT
- Alicense-qualityBmaintenanceLocal-first MCP server for indexing and searching research materials (papers, notes, logs, READMEs) using SQLite FTS, with tools for memory management and evidence retrieval.MIT
Related MCP Connectors
Token-efficient MCP memory for Markdown vaults. Tiered search, GraphRAG, AI memories.
Serve a folder of Markdown notes as an MCP server: hybrid search, reading, and sourced answers.
User-owned memory for AI agents, Copilot, Claude, IDEs, CLIs, and chat apps over remote MCP.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/abm9111/chamber'
If you have feedback or need assistance with the MCP directory API, please join our Discord server