An embeddable, event-sourced context graph with an MCP interface. Zero external dependencies by default (SQLite + Kùzu); scales to Postgres + Neo4j. Define your domain in one topology YAML.
Project description
One brain for every AI you use — private, portable, and yours.
What is this?
Every time you close a chat, your AI forgets everything. You re-explain your projects, your team, your decisions — over and over. And your knowledge ends up trapped inside whatever model you happened to use.
CanonGraph gives your AI a persistent, private memory that you control — a knowledge graph it builds as you work. Tell it something once and it remembers: your projects, the people involved, what was decided, the documents that matter. Next week, it already knows.
The key idea: the memory belongs to you, not to any one AI. It lives on your machine, in your own database, and any assistant can plug into it — Claude today, a different model tomorrow. Switch providers and you lose nothing, because your knowledge was never locked inside the model.
Think of it as a second brain your AI agents share — portable, private, and yours.
Who is it for?
Anyone tired of re-explaining their world to an AI every single session:
- 🧑💻 Developers — so your AI remembers your codebase, architecture decisions, and conventions.
- 🔬 Researchers & writers — a memory of papers, ideas, sources, and how they connect.
- 🏢 Teams — a shared, structured memory of clients, deals, or projects (scales from your laptop to a shared server).
- 🧠 Anyone who wants an AI that actually knows their world.
Works with the agents you already use. Claude Code and Claude Desktop get the smoothest setup (a one-command plugin and a guided wizard), and any MCP-capable client works out of the box — Cursor, Cline, and others. Anything that isn't MCP — a custom script, another model, your own app — connects over a plain REST API. One memory, many front ends.
No account, no cloud, no subscription. It runs locally, your data stays yours, and it's open source (Apache-2.0) — so you can build on it freely.
Try it in 60 seconds
See it before you set up anything. One command seeds a small, self-contained sample memory and opens it as a live, interactive graph:
uv tool install canongraph # or: pipx install canongraph / pip install canongraph
canongraph demo # seeds a sample memory, shows recall + its source, opens the viewer
You'll get an instant answer with its source right in the terminal —
You ask: "Why are we pursuing the Aurora deal?"
◆ CanonGraph recalls:
Acme Robotics is the category leader in warehouse automation… gross margins
have expanded from 48% to 61%… net revenue retention is 132%.
◈ source: aurora-ic-memo.pdf
— then the graph opens in your browser to explore. It's honest sample data in a dedicated, isolated demo profile: it never touches your real memory, and you can remove it any time (rm -rf ~/.canongraph/demo). When you're ready, point it at your world with canongraph init (below).
Prefer to stay in the terminal?
canongraph demo --no-openseeds and prints the recall moment without launching the viewer.
See it in action (about 2 minutes)
# 1. Install (one command)
uv tool install canongraph # or: pipx install canongraph / pip install canongraph
# 2. Create your memory (starts from a sample you can shape to your own world)
canongraph init
# 3. Connect it to Claude, and check it's healthy
canongraph config --client claude-desktop # prints the snippet to paste into Claude
canongraph doctor # → status: OK
💡 Want Claude to design the memory around your world for you? That's the
/context-initskill in the Claude Code plugin below — Claude interviews you ("what kinds of things do you want me to remember?") and sets it all up.canongraph initon its own just gives you a ready-to-use starter you can edit.
Now just talk to Claude normally. As you work, it remembers the important things. Later:
- "What do we know about the Acme deal?" → Claude recalls it from your graph.
canongraph view→ open a private local page and explore everything it remembers as a live, interactive graph — search it, click any node for its sources ("why do you believe this?"), scrub through time, watch new memory glow in as it's captured, and customize the HUD (theme, motion, node size, labels, freeze) to taste.canongraph redact <doc>→ tell it to forget something, for real.
Install the Claude Code plugin to skip the manual client config and get a
/context-initsetup wizard:uv tool install canongraph # the plugin needs this binary on PATH to run /plugin marketplace add jvattimo1/canongraph /plugin install canongraph@canongraph /context-initThe plugin registers the MCP server config for you — but it still needs the
canongraphbinary installed (e.g.uv tool install canongraph) for that server to actually run. The/context-initskill checks for it first and tells you if it's missing.
It keeps itself up to date
You don't hand-feed CanonGraph. As you work, your assistant captures the durable facts — decisions, people, projects, how they connect — into the memory, on your own model (the engine never calls an LLM). When it's unsure, it records the fact with low confidence and the pruning policy quietly demotes it out of trusted recall, so guesses never pollute what you get back. At the end of a session the whole conversation is preserved, searchable. Your memory stays current without you thinking about it.
Controls (all optional):
| Env var | Effect |
|---|---|
CANONGRAPH_NO_AUTOCAPTURE=1 |
Turn off all automatic capture at session end. |
CANONGRAPH_NO_AUTODEMOTE=1 |
Keep session-end capture but skip the unsure-fact demote. |
CANONGRAPH_DEMOTE_FLOOR=0.35 |
The confidence below which a fact is treated as unsure. |
CANONGRAPH_STRUCTURED_AUTOCAPTURE=1 |
(Claude Code) also run a structured background pass over the transcript. Off by default — it uses your model's tokens. |
Why it's different
Most "AI memory" either lives in someone else's cloud or dumps your chats into a pile of text. CanonGraph leads with what you get — the engineering is just the proof it's real:
| What you get | What it means · and why it's true |
|---|---|
| You own it | Runs 100% on your machine — your files, open source, no cloud, no account, no subscription. Proof: local database + local embeddings; nothing leaves your machine. |
| A real graph, not a note pile | Things and how they connect — and "Acme / Acme Corp / ACME" fold into one, instead of piling up. Proof: typed entities + relationships; it surfaces the existing entity so it's reused, not duplicated. |
| You can trust it | Ask "why do you believe this?" and see the source. Tell it to forget, and it's gone for real. Proof: an event-sourced log is the source of truth; redact/retract physically delete + scrub. |
| Any model, forever | Claude today, a different model tomorrow — over MCP or plain REST. Switch providers, keep your memory. Proof: MCP-native + a REST API over the same store; no lock-in. |
| It never quietly corrupts | Your memory is always reconstructable — nothing silently rots. Proof: the graph is a rebuildable projection of an append-only log. |
What you can build with it
- A personal second brain — an AI that remembers your projects, contacts, reading, and decisions.
- Deal / CRM intelligence — a graph of companies, people, and interactions your AI can reason over.
- A research assistant — papers, findings, and their connections, semantically searchable.
- Codebase context — architecture, conventions, and history your AI keeps straight.
Because it's Apache-2.0 licensed and headless (no locked-in UI), you can wrap it, embed it, or extend it however you like.
One brain, many agents
Run CanonGraph once as a shared service and point multiple agents at the same memory — e.g. Claude Code and another assistant working side by side on one knowledge base:
- Everyone reads at once. Lookups run concurrently — no waiting.
- Writes are safe. Saves are taken one at a time by a shared lock, so two agents writing at the same instant can never corrupt the memory or overwrite each other. Nothing is lost — a write just waits its (millisecond) turn. It's event-sourced, so the memory is always reconstructable.
- Facts merge instead of colliding. Because CanonGraph recognizes when two agents mean the same thing, if both add facts about "Acme" they fold into one clean record — not duplicates. Genuine collaboration, not a mess.
For a single user on one machine, the default embedded mode is all you need. For multiple agents (or a team) sharing one memory, flip to server mode — CG_TRANSPORT=http canongraph serve, backed by Postgres + Neo4j — and every agent connects over MCP or REST to the same brain.
The trade-off, honestly: writes are serialized (they take turns) rather than running truly in parallel. For a memory layer that's the right call — correctness first — and reads, the common case, are fully concurrent.
Many memories, one AI (opt-in multi-profile)
You can keep separate memories — e.g. work and personal — each its own profile with its own topology and data (--profile NAME, or canongraph profile use NAME). The MCP server serves one active profile, and isolation is the default: with no extra argument every tool reads and writes the active profile only, so your personal memory never bleeds into work.
When you want your AI to look across memories, it's opt-in and read-only:
list_profileslets the AI discover what memories you have — each profile's name, topology, entity/relationship/document counts, and which is active.- The log-store-answerable read tools —
get_topology,resolve,describe_entity, andsearch— take an optionalprofileargument. Given it, they answer from that profile, read-only, opened the same collision-safe way as the viewer (the durable log, never the other profile's graph — so it can't lock-collide with a running server and can never write to it). - Writes stay single-profile.
ingest/ingest_document/merge/alias/retract/redactoperate on the active profile only — there is no cross-profile write. To write into another memory, switch to it (canongraph profile use NAME). This is a safety boundary, not an oversight. - Graph-only tools stay active-only.
query(named Cypher) andbenchmarkrun against the active profile's live graph projection; asked to target another profile they return a clear message (switch profiles, or usesearch/resolve/describe_entity, which do cross).
One honest caveat about cross-profile search. Your active profile's search is full hybrid (vector + keyword). Cross-profile search is keyword-only (full-text) — by design: a non-active profile's vector index lives in its graph, which we deliberately don't open (collision-safe coexistence), so only its log store's full-text index is used. Expect comparable exact-term recall but weaker paraphrase recall than the active hybrid path; results are attributed by profile.
Working with a team
Two ways for multiple people to share memory — pick by whether you want one shared brain or your own brains that sync.
One shared brain (server mode). Run one CanonGraph backed by Postgres + Neo4j and point everyone's agent at it (MCP or REST, behind a shared token). Reads run concurrently; writes take safe turns via a cross-instance lock (event-sourced, so nothing's lost — see One brain, many agents). Every write records who made it (CG_ACTOR), and entity resolution folds same-named facts into one entity — so the team collaborates on a single, deduplicated, attributed knowledge base. Best when you want one source of truth.
Personal brains that sync (signed replication). Each person keeps their own local memory and shares by exchanging signed log segments — canongraph sync export → teammate canongraph sync import (see Team sync). Segments are HMAC-signed with a shared team key, carry each event's actor + an idempotency key (re-importing is a no-op), and merge additively — non-conflicting facts converge across the team, and a retraction survives re-sync. Best when you want data ownership + selective sharing (like git for memory). You can combine the two: personal brains that also push segments to a shared one.
Honest limits (today) — these are trusted-team features, not enterprise multi-tenant:
- No per-user access control within one memory. Shared server mode uses one token — no per-user login, roles, or row-level permissions yet; everyone with the token sees and writes everything.
- Server-backed profiles share one database. Isolation between memories is an embedded (per-directory) property today; per-tenant isolation on the server backend is future work.
- Sync uses a shared secret and is manual. One HMAC team key (not per-user asymmetric keys), via export/import (or a script), not real-time.
- Additive merge, not a CRDT. Concurrent edits to the same field resolve last-writer-by-import.
These and the other current v1 boundaries are documented plainly in LIMITATIONS.md; where they're headed is on the roadmap.
How it works
You never manage the memory by hand — Claude does it for you:
You talk to Claude ─────────► Claude notices what's worth remembering
│
┌─────────────────────────┴─────────────────────────┐
▼ ▼
Structured facts The raw content
(Acme is a company in the (the note / doc, kept whole
cloud sector, advised by…) and semantically searchable)
│ │
└──────────────► your private memory graph ◄────────┘
│
Later: "what do you know about X?" ──────┘ → Claude recalls, with sources
Two things make this trustworthy:
- You define the shape once — but you don't have to up front. A topology is not required to start: every memory ships with a generic starter shape, so you can capture from minute one. When you want structure, the
/context-initskill (in Claude Code) interviews you — "what kinds of things do you want me to remember?" — and sets it up; prefer the terminal?canongraph initstarts from an editable sample, orcanongraph init --from-spec your.yamluses your own. As your needs grow,canongraph extendadds new kinds of things safely. - You stay in control. Everything Claude remembers carries its source ("why do you believe this?"), you can browse it (
canongraph view), and you can redact a document (canongraph redact) — its text, chunks, and embeddings are removed from the active store, and freed pages are best-effort scrubbed. This is strong hardening, not a cryptographic erase guarantee: at-rest confidentiality still relies on full-disk encryption, and redaction removes the document — not any structured facts already extracted from it (see SECURITY.md for the exact boundary).
Two layers, one memory
- Structured layer — the tidy graph of things and relationships you query precisely.
- Document layer — anything else you throw at it (notes, files, transcripts) kept whole and searchable by meaning.
Nothing is lost just because it didn't fit a neat category — and the things you care about stay clean and queryable.
Under the hood (for the curious)
CanonGraph is event-sourced: everything you tell it is appended to an immutable log — the single source of truth. The queryable graph and the search index are projections rebuilt from that log, so your memory is always reconstructable and never silently corrupted.
your topology (what to remember)
│
▼
┌───────────────┐ ┌──────────────┐ ┌───────────────────┐
│ event log │ ──► │ projector │ ──► │ knowledge graph │ ──► MCP tools
│ (source of │ │ (rebuildable │ │ + hybrid search │ (Claude works
│ truth) │ │ projections)│ │ │ through these)
└───────────────┘ └──────────────┘ └───────────────────┘
How search works (hybrid, graph-aware)
search is hybrid: it runs two retrievers over your document chunks and fuses them, so it
handles both fuzzy paraphrase questions and precise exact-term lookups (codenames, ticket
numbers, identifiers) — the cases pure vector search ranks poorly.
- Vector retriever — semantic kNN over local chunk embeddings (meaning-based recall).
- Keyword retriever — lexical full-text search over the same chunks (SQLite FTS5 embedded / Postgres tsvector + GIN on the server), with stemming, so a rare exact token reliably surfaces its document.
- Reciprocal Rank Fusion (RRF) — the two rankings are merged rank-by-rank (not by raw score), sidestepping their incompatible scales; a chunk both retrievers agree on rises to the top.
Each returned passage also carries mentions: the graph entities that chunk is linked to
({id, name, type}) — graph context, not just text.
What score means: the RRF fused score (higher = more relevant). It combines the two
rankings, so it is not a cosine similarity or a probability — treat it as a relative
ordering signal within one response, not an absolute threshold. The keyword index is a
rebuildable projection kept in lockstep with the chunk set on every ingest and rebuild.
Optional precision upgrade: with the [st] extra installed, pass rerank=true to reorder
the fused top passages with a cross-encoder. It's skipped gracefully when [st] isn't present
— the lightweight default path needs no torch.
Time & recency (temporal reads over the log)
Because the log is append-only and every event is timestamped, "what did I know / what was true, and when?" is answered by reading the log — no history table, no schema migration.
-
observed_at(valid time). Anyingestevent oringest_documentcall may carry an optionalobserved_at(ISO timestamp) recording when the fact was true in the world, as distinct from its append/transaction time (ts). It rides the existing provenance channel (_prov), so legacy events stay byte-identical; when absent it defaults totson read. -
History view (
describe_entity).describe_entityreturns ahistory: the ordered value-by-value timeline of an entity, each value carrying itsobserved_atand a derivedsuperseded_at(when a later event replaced it;nullfor the current value). Derived read-only from the log — no new storage. -
As-of queries.
describe_entity(id_or_name, as_of=<ISO timestamp>)returns the entity's state as of that time — an ephemeral in-memory replay of the events appended by then that never touches the materialized tables, the graph projection, or any watermark. It applies the merge/alias markers up to that time exactly as a real replay does (a merged-away entity resolves into its winner — no phantom). v1 uses transaction time (ts, "what the log knew by then"); ifas_ofpredates the entity's first event the response reports it did not exist yet. Retracted entities are physically purged from the log, so an as-of query for a time before a retraction cannot reconstruct them (they report absent) — an unavoidable v1 limitation. -
Search time-decay (recency, opt-in). Set
CG_SEARCH_DECAY_HALFLIFE_DAYS(or passdecay_halflife_daystosearch) to a positive number to rank recent documents higher: each fused RRF score is multiplied by an exponential decay0.5**(age_days / halflife)on the document's age (itsobserved_at, else appendts). Off by default — equal-age corpora are unaffected (the retrieval eval is unchanged with decay on), so it only helps when your documents actually span time. -
Runs anywhere, zero setup by default. The default backend is fully embedded — SQLite + Kùzu — so
pip installand go. No Docker, no database to run. Embeddings are computed locally (no API key, no data leaving your machine). -
Scales to a team by flipping one setting to the Postgres + Neo4j backend — same code, same tools.
-
Describe your world in one YAML file (a "topology"). Swap it to repurpose the same engine for any domain; the engine code never changes.
-
Speaks MCP (the Model Context Protocol), so any MCP-capable assistant can use it — no custom integration.
Commands
| Command | What it does |
|---|---|
canongraph init |
Set up a new memory (from a sample, or your own topology) |
/context-init (plugin skill) |
Have Claude interview you and design the memory around your world |
/recall <question> (plugin skill) |
Answer a question from your memory with grounded, provenance-checked results — resolve the entities, retrieve + traverse, verify each fact's source, and cite them (flags gaps as "not in memory"; read-only) |
canongraph config |
Print the snippet to connect it to your AI client |
canongraph doctor |
Health check — is everything wired up? |
canongraph view |
Open a private local page to explore your memory — an interactive graph you can search, drill into for sources, and scrub through time (live-updating, read-only, loopback-only) |
canongraph list / show <thing> |
Inspect what's remembered (with sources) |
canongraph redact <doc> |
Forget a document — permanently |
canongraph retract entity <type> <key> / retract relationship <rel> <from> <to> |
Forget a wrong structured fact — an entity (its relationships cascade) or one relationship. Retracting a merge winner / alias target is refused (v1 boundary — retract before merging/aliasing) |
canongraph extend |
Add new kinds of things to your memory, safely |
canongraph sync export / sync import <segment> |
Share memory with a teammate via a signed, deduplicated log segment (see Team sync) |
canongraph ingest-transcript <path> |
Record a Claude Code session as searchable memory — extractive, idempotent (see Automatic capture) |
canongraph serve |
Run the memory server (what your AI connects to) |
Run canongraph -h for the full command + flag reference. Every command takes --profile NAME to target a specific memory.
Team sync (signed log replication)
Two teammates can share their memory by exchanging signed, deduplicated log segments. Because the event log is the source of truth, replication is just: export a signed slice of the log → your teammate verifies and imports it → their projection rebuilds.
export CG_SYNC_KEY="a-shared-team-secret" # both teammates use the SAME key (never commit it)
export CG_ACTOR="alice" # your identity — every fact records who created it
# Alice exports everything (or everything since a cursor) to a file:
canongraph sync export --out alice-segment.json
canongraph sync export --since 42 --out alice-delta.json # only events past event_id 42
# Bob verifies + imports it into HIS memory, then his graph rebuilds:
canongraph sync import alice-segment.json
What's guaranteed:
- Attribution — every event records its real author (
CG_ACTOR, or the profile'sactor, else your OS login). Imported facts keep the originating teammate's actor. - Signed + fail-closed — each segment carries an HMAC-SHA256 signature over its contents, keyed by the shared
CG_SYNC_KEY. A tampered segment, one signed with a different key, or one built for an incompatible topology is rejected — importing nothing. - Idempotent — every event has a stable content-hash key, so re-importing the same segment (or overlapping segments) adds 0 the second time.
Merge semantics — an honest v1 boundary. Imported facts are appended to your local log and replay in local arrival order. Non-conflicting facts converge: teammates adding facts about different entities (or additive facts) all reach the same union after a full exchange, and same-named entities fold together via entity resolution. But concurrent edits to the same field of the same entity are not auto-reconciled — the result is last-by-local-replay-order, not a principled merge. This is signed additive replication, not a CRDT. See SECURITY.md.
Automatic capture
So you never have to remember to say "remember this," CanonGraph can automatically record each Claude Code session as searchable memory when the session ends. A SessionEnd hook (shipped in the plugin) runs canongraph ingest-transcript on the session transcript, which pulls out the substantive user and assistant turns — dropping tool calls, tool output, thinking, and system reminders — and stores them as one searchable session document. Later you can ask "what did we work on last Tuesday?" and search will surface it.
Be honest about what this is. This is extractive: it records/captures the session as searchable memory — it does not AI-summarize it. CanonGraph's engine has no LLM. The proactive capture skill is the thing that extracts structured facts (entities, relationships, decisions with provenance) during a session; this hook is the automatic safety-net that preserves the session itself so nothing is lost.
It's designed to be non-intrusive: SessionEnd is non-blocking, and the hook swallows every error and always exits 0 — a capture failure can never wedge Claude Code. It no-ops cleanly if the canongraph binary isn't installed. Re-capturing the same session updates its one document (a stable per-session doc_id) instead of duplicating it.
Run it by hand any time:
canongraph ingest-transcript ~/.claude/projects/<project>/<session-id>.jsonl
canongraph ingest-transcript <path> --dry-run # show what it WOULD capture, write nothing
Enable / disable. The hook is active whenever the plugin is installed. To turn it off, either set CANONGRAPH_NO_AUTOCAPTURE=1 in your environment, or remove/rename hooks/hooks.json in the plugin. (Respects CG_READ_ONLY=1, which refuses the write entirely.)
The tools your AI uses
Read: get_topology · resolve (find the canonical thing) · describe_entity (one entity — its history + provenance) · query · benchmark · search (hybrid: vector + keyword) · list_profiles (discover your separate memories).
Write: ingest (remember a fact) · ingest_document (remember a document) · merge / alias (unify duplicates) · retract (forget a fact) · redact (forget a document).
Isolation is the default — every tool reads/writes the active profile only. The read tools (list_profiles, get_topology, resolve, describe_entity, search) accept an opt-in profile for a read-only cross-profile look; writes stay single-profile. See Many memories, one AI.
Getting data in
There are exactly two ways data gets into CanonGraph — plus one automatic path — and none of them needs a framework. A connector is anything that calls the API; there is no plugin system to learn.
1. LLM-driven (the common case) — the LLM is the connector, no code.
Your assistant reads a source with its own MCP tools (Gmail, Slack, GitHub, a local file, a web page) and then calls CanonGraph's ingest / ingest_document tools to remember what matters. Nothing to install between them — that's why there is no connector SDK. For example, you just say:
"Read the last few messages in our #launch Slack channel and remember who owns each launch task."
The LLM pulls the messages with its Slack tool and writes the people, tasks, and ownership as facts via ingest.
2. Programmatic (automated / scheduled / bulk) — a script POSTs to the REST API.
Any script, in any language, that can make an HTTP request is a connector. POST facts to /api/v1/ingest or a document to /api/v1/ingest_document with Authorization: Bearer $CG_AUTH_TOKEN. That script is your connector — the REST/MCP surface is the connector interface. A one-liner that remembers a document:
curl -H "Authorization: Bearer $CG_AUTH_TOKEN" -H "Content-Type: application/json" \
-d '{"doc_id":"notes/kickoff.md","text":"Kickoff: Ada owns the launch.","source":"kickoff.md"}' \
http://127.0.0.1:8000/api/v1/ingest_document
Two copyable, dependency-free reference connectors live in examples/: ingest_folder.py (walk a folder → POST each .md/.txt as a document) and ingest_facts.py (POST structured facts). They're starting points to copy, not a framework — see examples/README.md.
3. Automatic (no action at all). With the plugin installed, each Claude Code session is captured as searchable memory when it ends — see Automatic capture above. So you never have to remember to say "remember this."
The full REST/MCP surface — the interface every connector targets — is below.
Use it from any model — MCP or REST
Your memory, any model, you own it. CanonGraph isn't locked to one assistant:
- MCP — works out of the box with any MCP-capable client (Claude Code, Claude Desktop, Cursor, Cline). No custom integration.
- REST — a thin JSON/HTTP API over the same memory, so any app or model — a raw OpenAI/Gemini script, LangChain, a shell one-liner — can read and write it over HTTP. No MCP required.
Run the server over HTTP (CG_TRANSPORT=http canongraph serve) and it serves both the MCP endpoint and the REST API on the same port, behind the same bearer token (CG_AUTH_TOKEN):
curl -H "Authorization: Bearer $CG_AUTH_TOKEN" \
-d '{"query":"what drove revenue growth"}' \
http://127.0.0.1:8000/api/v1/search
REST routes (all under /api/v1, all requiring Authorization: Bearer $CG_AUTH_TOKEN):
| Route | Method | Tool |
|---|---|---|
/api/v1/topology |
GET | discover entities, queries, metrics |
/api/v1/search |
POST | hybrid search (vector + keyword, RRF) {query, top_k?} |
/api/v1/query |
POST | named query {query_id, params?, limit?, offset?} |
/api/v1/resolve |
POST | canonical lookup {entity_type, key|resolution_keys} |
/api/v1/benchmark |
POST | metric stats {metric, limit?} |
/api/v1/entity/{id_or_name} |
GET | browse one entity |
/api/v1/ingest |
POST | remember facts {events} (write) |
/api/v1/ingest_document |
POST | remember a document {doc_id, text, source?, links?} (write) |
/api/v1/redact |
POST | forget a document {doc_id} (destructive) |
Auth is fail-closed (401 without/with a wrong token; the server refuses to start unauthenticated unless CG_ALLOW_ANONYMOUS=1), /health and /ready stay open for probes, and CG_READ_ONLY=1 turns the three write/destructive routes into 403 — the same controls as the MCP transport.
Install
uv tool install canongraph # recommended — a stable, always-available command
pipx install canongraph # alternative
pip install canongraph # or a plain pip install
pip install "canongraph[st]" # optional: higher-quality embeddings (larger download)
pip install "canongraph[server]" # optional: the Postgres + Neo4j team backend
Choosing an embedder. By default CanonGraph uses a small local model (model2vec — no torch, no API). Set CG_EMBEDDER to switch: st for the higher-quality sentence-transformers path (needs [st]), or a pkg.module:attr import spec to plug in your own — a class, a zero-arg factory, or a ready instance — so a deployment with existing embedding infrastructure can reuse it instead of running a second pipeline:
CG_EMBEDDER="mycompany.embeddings:ProductionEmbedder" canongraph serve
A custom spec loads and runs the Python you point it at — an operator-controlled extension point (you're loading your own code), so only set it to code you trust.
From source (to hack on it, or to try it before it's on PyPI):
git clone https://github.com/jvattimo1/canongraph.git
cd canongraph
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest # run the test suite
canongraph init && canongraph doctor
Your data & privacy
Your memory lives in plain files under ~/.canongraph/ on your own machine (created private — 0700/0600). Nothing is sent anywhere. canongraph redact physically removes content — text, chunks, and embeddings — so "forget" means forget. The one network call is a one-time download of the local embedding model from the HuggingFace Hub on first use (pinned to an exact commit, then cached). Details in SECURITY.md.
Build on it
CanonGraph is Apache-2.0 — use it, embed it, fork it, ship it, commercially or not. It's headless and MCP-native on purpose, so it's easy to build on. Contributions welcome — see CONTRIBUTING.md.
License
Apache License 2.0. All demo data is synthetic.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file canongraph-0.1.1.tar.gz.
File metadata
- Download URL: canongraph-0.1.1.tar.gz
- Upload date:
- Size: 385.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a31ecb8c453cc04a23acf6dd25ffbc28dd1838c29aa4b42d810fb2af3613f504
|
|
| MD5 |
043772cbaa1f5549a55834f7cb47029c
|
|
| BLAKE2b-256 |
6bd86c136d8b668130da4adc373b011d4a55be2822e67fe4af864221d03e7a8b
|
File details
Details for the file canongraph-0.1.1-py3-none-any.whl.
File metadata
- Download URL: canongraph-0.1.1-py3-none-any.whl
- Upload date:
- Size: 228.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c4186b499f094df74b322bd0571b87263b8249998ae2c4e36596dce8da1e458b
|
|
| MD5 |
c02b0004a17452c228d4904bf72b5539
|
|
| BLAKE2b-256 |
acc9c459cf2d6466a6f8cf92cf4a398e57b8e67d03cbf7a01013cd73565fbabb
|