Circadia
БесплатноПоддерживаетсяMarkdown-vault agent memory with a derived, brain-inspired graph index (hippocampal indexing, spreading activation, bi-temporal facts).
Описание
Markdown-vault agent memory with a derived, brain-inspired graph index (hippocampal indexing, spreading activation, bi-temporal facts).
README
Circadia
Long-term memory for AI agents, kept as markdown notes you can read, with an index that recalls the way people do.
[!WARNING] Circadia is under active development. Don't use it in production until v1.
What is this?
A language model forgets everything when a conversation ends. An agent memory system gives it somewhere to write things down and a way to find them again later, so it can pick up where it left off, remember your preferences, and know that the service it helped you move last month now runs somewhere else.
Most memory systems keep that memory in a database you can't easily read. Circadia keeps it in a folder of plain markdown files (a vault) that you can open in any editor or in Obsidian, fix by hand, and track in git. A small index sits next to the vault and does the searching. You can delete the index at any time and rebuild it from the notes.
The name comes from the circadian rhythm. Like a brain, Circadia writes down what happened during the day, then sleeps on it: a nightly job turns the day's raw notes into stable facts. Old facts are struck through and kept, never deleted, so the history stays readable.
Why Circadia
- You can read your agent's memory. Every memory is a markdown file. Nothing is hidden in an opaque store, and a human can always correct it.
- Recall follows associations, not just keywords. A question lights up a few matching notes, and that activation spreads along the links between notes, the way one memory reminds you of another (personalized PageRank, as in HippoRAG).
- Facts know when they were true. Each fact records when it was true in the world and when the vault believed it, so you can ask "where did this run in July?" and get July's answer (bi-temporal facts).
- Agents can't write facts directly. Agents write episodes, raw notes about what happened. Only the nightly consolidation can promote them to facts, and untrusted sources wait for a human. One poisoned web page can't become a trusted memory.
- Every fact says where it came from. Who stated it, which episode it came from, and how much to trust it. Low-trust memories are fenced off when they're handed to a model.
- Small and local. Zero runtime dependencies. It runs on Node's built-in SQLite, fits on a Raspberry Pi, and talks to agents over MCP. Model calls (consolidation, optional extraction and embeddings) default to a local llama.cpp server.
How it works
The design copies one idea from neuroscience: the hippocampus doesn't store memories, it stores a sparse index of pointers to content held elsewhere in the brain. In Circadia the vault holds the content and the SQLite index holds only pointers, links and scores.
┌──────────────────── Agent (MCP client) ────────────────────┐
│ remember → writes an episode recall → reads index │
└────────────┬───────────────────────────────────┬────────────┘
▼ ▼
┌── Vault (markdown, git, Obsidian) ──┐ ┌── Index (SQLite, derived) ──┐
│ episodes/ append-only events │──▶│ nodes: notes, passages │
│ entities/ notes + typed facts │ │ edges: link, fact (bi- │
│ schemas/ consolidated summaries │ │ temporal), provenance, │
│ procedures/ how-tos │ │ triple (hipporag) │
└──────────────▲──────────────────────┘ │ FTS5 / BM25 keyword index │
│ └─────────────────────────────┘
Consolidation ("sleep"): nightly replay of episodes → facts, with
schema-fit gating, bi-temporal invalidation, one git commit per run
Get started
You need Node.js 26 or newer. Nothing else: SQLite is built into Node, and there are no runtime dependencies to install.
There are two first-class ways to run Circadia. They run the same program; they differ in how much of your machine the process can touch.
npx / global install |
Docker from GHCR | |
|---|---|---|
| setup | npx circadia …, or npm install --global circadia |
pull ghcr.io/therebelrobot/circadia |
| runs | on the host, as you | in a container, as a non-root user |
| filesystem | the host's filesystem | read-only root (via --read-only); the vault is the only writable mount, plus an ephemeral /tmp tmpfs |
| network | the host's network | none by default |
| isolation | your user account only | the process can only write to the vault |
npx / global install is the simplest: it runs on the host with the host's filesystem and
network access. Docker from GHCR is the better-isolated option: the MCP server runs
non-root with no network by default, and the vault is the only writable mount (plus an
ephemeral /tmp tmpfs). The read-only root filesystem comes from the --read-only run flag,
not the image itself — the image is built to be run that way. It cannot install packages or
reach the network unless you allow it.
Try it in a minute (npx)
npx circadia init my-memory # scaffold a vault: folders, config, templates
Write your first memory. Any markdown file with a little frontmatter works:
cat > my-memory/entities/projects/garden-sensors.md <<'EOF'
---
type: entity
kind: project
created: 2026-09-01
---
# Garden sensors
Soil-moisture sensors in the vegetable beds. Readings go over MQTT to a small
collector that decides when to water.
## Facts
- [runs_on:: [[raspberry-pi]]] [valid:: 2026-09-01..] [by:: user]
EOF
Index it and ask a question:
npx circadia index --vault my-memory
npx circadia recall --vault my-memory "where does the garden collector run"
npx circadia recall --vault my-memory --context "garden sensors" # formatted for an LLM
npx circadia timeline --vault my-memory garden-sensors # every fact, in time order
[!NOTE]
npxand global installs need circadia 0.3.2 or later. Earlier releases only ran from a clone (see ADR-0012).
Install it
npm install --global circadia
circadia --help
Run it in a container (Docker)
A multi-arch (amd64 and arm64) image runs the same stdio MCP server, published to GHCR on
v* tags. It is the same program as the npm package, packaged differently; its value is
isolation. The container runs non-root with no network by default, and the vault is the only
writable mount (plus an ephemeral /tmp tmpfs), so the process can only write to the vault.
The read-only root filesystem comes from the --read-only run flag, not the image itself; the
image is built to be run that way.
Run any command in it with the hardened invocation:
docker run --rm --read-only --tmpfs /tmp --cap-drop ALL \
--security-opt no-new-privileges --network none \
--user <uid>:<gid> -v <vault>:/vault \
ghcr.io/therebelrobot/circadia:<version> index --vault /vault
Pin a version tag, or better an image digest. --user <uid>:<gid> must match the owner of the
vault on the host so the container can write episodes and the index; find it with id -u and
id -g.
Network. --network none is correct for the default configuration, which makes no network
calls. Drop it only when embeddings are enabled, and then attach the container to a network
that reaches only the embedding endpoint. Note that 127.0.0.1:8080 inside the container is
the container itself: point embeddings.endpoint at http://<docker-host>:8080 or a service
name on that network.
Nightly consolidation. The image contains no scheduler. Run consolidate from a host cron
job or systemd timer with the same image:
docker run --rm --read-only --tmpfs /tmp --cap-drop ALL \
--security-opt no-new-privileges --network none \
--user <uid>:<gid> -v <vault>:/vault \
ghcr.io/therebelrobot/circadia:<version> consolidate --vault /vault
Verification. The image is published with signed build provenance when a v* tag is
pushed:
gh attestation verify oci://ghcr.io/therebelrobot/circadia:<version> --owner therebelrobot
The GHCR package starts private and must be made public once, in its package settings.
Connect it to your agent (MCP)
circadia mcp is an MCP server over stdio. It exposes recall, remember, timeline,
relate, get_note, and the dream tools (wake, endorse_dream, dismiss_dream). Add it
to any MCP client's server list. Pick either transport.
On the host with npx:
{
"mcpServers": {
"circadia": {
"command": "npx",
"args": ["-y", "circadia", "mcp", "--vault", "/path/to/my-memory"]
}
}
}
With a global install, use "command": "circadia" and drop -y and circadia from args.
In the sandboxed container (the flags are explained under Run it in a container):
{
"mcpServers": {
"circadia": {
"command": "docker",
"args": [
"run", "-i", "--rm", "--read-only", "--tmpfs", "/tmp", "--cap-drop", "ALL",
"--security-opt", "no-new-privileges", "--network", "none",
"--user", "<uid>:<gid>", "-v", "<vault>:/vault",
"ghcr.io/therebelrobot/circadia:<version>"
]
}
}
}
The server speaks only stdio, so it has no network port to secure. Agents can write episodes but never facts (docs/SECURITY.md). For a TypeScript agent, see the Mastra example.
Several agents
One vault shared by several agents lets them pollute each other: a throwaway spike becomes a project fact, and one agent's recalls raise another's activation. A workspace gives each agent its own vault while keeping shared memory in a defined place.
circadia workspace init ~/memory # registry + a global vault
circadia workspace add --workspace ~/memory --project work
circadia workspace add --workspace ~/memory --project work --agent coder
circadia mcp --workspace ~/memory --project work --agent coder # pin one agent
A workspace is a folder of ordinary vaults plus circadia.workspace.json, which places each
vault on two axes, project and agent. An agent bound to (work, coder) reads from
its lineage — work.coder, work, coder, global — and never from a sibling such as
work.architect. Recall runs once per vault in the lineage and merges the results, labeling
each hit with its layer. Shared memory is marked by where it lives: a fact in work is
shared with every agent on work, and a fact in global with every agent. Agents write
episodes to their own cell by default; a human moves a fact into a shared layer with
circadia lift. Plain --vault keeps working unchanged. See
docs/WORKSPACES.md and
RFC-0004.
Run it as a service (HTTP MCP)
For agents that cannot spawn a stdio child — a service on another machine, a laptop reaching
a Pi on the LAN — circadia serve runs the same MCP tool surface over Streamable HTTP
(POST /mcp) as a long-lived process. It adds a built-in nightly consolidate scheduler, so
you no longer need a host cron job. Everything runs in one process under one write lock
(RFC-0003).
serve requires a bearer token. It refuses to start without one, with a token under 32
bytes, or with a read token equal to the write token:
head -c 48 /dev/urandom | base64 > ~/.circadia-write-token # a Docker secret
CIRCADIA_WRITE_TOKEN_FILE=~/.circadia-write-token \
circadia serve --vault ~/my-memory
By default it binds 127.0.0.1 (loopback). To reach it from another machine on your LAN, set
serve.bind to "0.0.0.0" (or CIRCADIA_BIND=0.0.0.0). The token is then the only thing
standing between the LAN and your memory, so keep it secret and prefer a reverse proxy that
terminates TLS:
{ "serve": { "bind": "0.0.0.0", "port": 8711 } }
The optional read token (CIRCADIA_READ_TOKEN_FILE) covers only recall, timeline,
relate and get_note; remember, wake, endorse_dream and dismiss_dream need the
write token. The nightly run is opt-in — serve.schedule.consolidate defaults to null, so
nothing runs until you set a local "HH:MM":
{ "serve": { "schedule": { "consolidate": "03:00", "timezone": "America/New_York" } } }
circadia serve --check reads a local heartbeat file (no network route) and exits non-zero
when the service is unhealthy, for a container HEALTHCHECK or a systemd check. See
docs/CONFIG.md §serve for every key and docs/SECURITY.md T2 for the controls.
Container. The circadia-server image is the stdio image plus a serve entrypoint, with
the same hardening (non-root, read-only root, the vault the only writable mount). A compose
example is in docker-compose.server.yml:
docker compose -f docker-compose.server.yml up -d
Workspaces. serve --workspace <dir> --project p --agent a serves a workspace the same
way the stdio server does (pinned, or --select-per-request). The scheduler runs against the
bound vault.
Build from source
git clone https://github.com/therebelrobot/circadia.git
cd circadia
npm install # dev tools only: typescript, @types/node, the MCP SDK for tests
npm test # the full suite
npm run typecheck
npm run example:index # index the bundled example vault
npm run example:recall -- "where does the orchard collector run"
node bin/circadia.mjs --help # the CLI runs straight from source, no build step
From a clone, TypeScript runs directly with Node's type stripping. The only build is the
one npm pack runs for publishing (npm run build emits plain JS to dist/). To use your
checkout as the global circadia command, run npm link.
Releasing
Releases are cut automatically when a PR merges to main, driven by a label on the PR:
| label | bump |
|---|---|
release:major |
x+1.0.0 |
release:minor |
x.y+1.0 |
release:patch |
x.y.z+1 |
On merge, the release workflow bumps package.json,
commits chore: release vX.Y.Z, tags vX.Y.Z on main, and creates a GitHub Release. Because
a tag pushed with the built-in GITHUB_TOKEN does not trigger other workflows, the release
workflow then dispatches npm-publish.yml (npm with
provenance) and container-publish.yml (GHCR, both
images) on the new tag with gh workflow run. workflow_dispatch is the one trigger
GITHUB_TOKEN can still fire, so no PAT or GitHub App token is needed — the same workflows
also publish normally when a human pushes a tag by hand. A PR with no release:* label merges
silently, so docs and chore PRs do not churn versions. Precedence is major > minor > patch.
One-time setup: the three labels already exist; if branch protection blocks direct pushes to
main, let the GitHub Actions app bypass it (via rulesets) or the release commit is rejected.
The release:patch|minor|major npm scripts remain for a manual release from a clone.
Open it in Obsidian
Open the vault folder as an Obsidian vault and set the templates folder to
_meta/templates. Install the Dataview plugin to query facts inside Obsidian:
TABLE runs_on, valid FROM "entities" WHERE runs_on
A tour
Recall: cue, spread, rank
- Cue. Keyword search (SQLite FTS5, or a built-in BM25) and entity names in the question pick a few starting notes, the seeds. Optional embeddings add a third list.
- Spread. Activation flows out from the seeds along links and facts (personalized PageRank). Related notes light up even when they share no words with the question.
- Rank. Scores combine graph activation, how recently and often a note was used (ACT-R activation, so unused memories fade without being deleted), and note importance.
- Budget. The best passages are packed into a token budget and handed to the model. Every hit shows its score breakdown, so rankings are explainable.
Details: docs/RETRIEVAL.md.
Time: facts have two clocks
## Facts
- [runs_on:: [[pi-cluster]]] [valid:: 2026-08-11..] [by:: user] [src:: [[2026-08-11-migration]]] ^f-orch-host
## History
- ~~[runs_on:: [[old-laptop]]] [valid:: 2026-06-01..2026-08-11]~~ [superseded:: 2026-08-11] [by:: user]
The subject is the note the fact lives in. valid is world time: when the fact was true.
at and superseded are system time: when the vault believed it. --as-of answers as
the vault stood and as the world was at that moment:
circadia recall --vault my-memory --as-of 2026-07 "where did it run"
Full grammar: docs/SCHEMA.md §4.
Sleep: from episodes to facts
Agents call remember, which writes an episode: an append-only note about what
happened. circadia consolidate (run it nightly from cron or a systemd timer) replays new
episodes, extracts candidate facts with a small model (set extraction.provider first;
it is off by default), and runs each through a schema-fit gate:
- a fact about a known entity, with a known predicate, that contradicts nothing is promoted;
- anything novel, contradictory, or from an untrusted source (
web,tool) waits in a queue for you, incircadia review.
Promoted facts cite their episode (src::), and the whole run is one git commit you can
read or revert.
Graph modes: pay for structure only when you need it
| mode | uses | cost | when |
|---|---|---|---|
wikilink |
links you wrote | none | default first rung; most everyday recall |
typed |
+ typed, time-stamped facts + provenance | none | as-of queries, relationships, multi-entity cues |
hipporag |
+ LLM-extracted phrase graph (triples) | LLM + cache | deep or research topics, scoped by tag or path |
You choose how much structure to build per note (frontmatter graph:, then the first
matching graph.scopes rule, then graph.defaultExtraction). Each query then chooses how
much to use. In the default auto mode it climbs wikilink → typed → hipporag only
when the result looks weak: too few seeds, no clear winner, several entities named, or a
time-travel query.
Dreaming: loose associations, never facts
Real sleep has a second phase. REM sleep recombines distant memories, which may be why
sleep helps with creative leaps. circadia dream pairs recently active notes with distant,
older ones and asks the model whether anything connects them. The answer is a candidate
association, stored outside the vault, read once with circadia wake, and given zero
weight in recall until the eval shows it helps. Only you can turn one into a fact, in
circadia review. See RFC-0001.
Inspired by the brain
Each mechanism maps to a finding from cognitive science. The reasoning is in docs/ARCHITECTURE.md, and every source is in docs/SOURCES.md.
| Brain | Circadia |
|---|---|
| Hippocampal indexing: the hippocampus stores pointers to cortical content | The vault holds content; the SQLite index holds only pointers, edges, and scores, and can be rebuilt from the vault |
| Complementary learning systems: fast episodic store, slow semantic consolidation | Episodes are written immediately; facts only come from consolidation |
| Spreading activation (Collins & Loftus) | Personalized PageRank from cue-matched seeds (as in HippoRAG) |
| ACT-R base-level activation: recency × frequency, power-law decay | Access log → activation term in ranking; forgetting demotes, never deletes |
| Reconsolidation: recall makes a memory labile | Every recall is logged; contradicted recalls get flagged for revision |
| Source monitoring: false memories are mostly source errors | Every fact records by::, src::, trust::; low-trust recall is fenced as data |
| Event segmentation | Episodes are cut at topic shifts, not token counts |
| Schemas accelerate consolidation | Schema-fit gate: facts that fit known entities merge fast; novel ones need corroboration |
| Sleep has two phases: NREM replay consolidates, REM recombines across schemas | A REM pass pairs recently active notes with distant ones; output is a candidate association, never a fact |
Status
Phases 1–8 of the roadmap are implemented and tested:
| phase | what it added |
|---|---|
| 1 | Vault schema v1, parser, linter, indexer, configurable graph modes, recall pipeline, untrusted-content fencing, example vault |
| 2 | Incremental indexing, watch, embeddings, relate and timeline, graph cache, benchmarks |
| 3 | MCP server, event segmentation for transcripts, Mastra example |
| 4 | Consolidation ("sleep") with the schema-fit gate, bi-temporal supersession, one commit per run, review |
| 5 | HippoRAG triple extraction, synonym edges, recognition-memory seed filter, triple promotion |
| 6 | Git-backed --as-of for prose, history, access-log compaction |
| 7 | circadia eval: recall@k and MRR per mode and query kind, tuning, ablations, LongMemEval and LoCoMo adapters |
| 8 | Dreaming: REM pass, read-once wake, human confirmation, weight-0 dream edges |
Retrieval numbers and their limits are in docs/EVAL.md and docs/PERFORMANCE.md.
Command reference
circadia init <dir> # scaffold a vault
circadia lint --vault <dir> # check against the schema
circadia index --vault <dir> [--full] # incremental index (or full rebuild)
circadia watch --vault <dir> # reindex on change
circadia recall --vault <dir> "<query>" # --as-of, --mode, --scope, --context, --json
circadia relate --vault <dir> <a> <b> # shortest paths between two notes
circadia timeline --vault <dir> <entity> # fact history
circadia history --vault <dir> <id> # a note across git commits
circadia consolidate --vault <dir> # nightly "sleep" (--dry-run, --dream)
circadia review --vault <dir> # accept or reject queued candidates
circadia dream --vault <dir> # REM pass (--sample-only makes no model calls)
circadia wake --vault <dir> # read the night's dream log once
circadia extract --vault <dir> # hipporag triple extraction
circadia mcp --vault <dir> # MCP server over stdio
circadia serve --vault <dir> # HTTP MCP service (--check for health)
circadia stats --vault <dir>
circadia eval # retrieval eval (see docs/EVAL.md)
circadia --help # every option
Model-backed commands (consolidate, extract, dream) call an OpenAI-compatible HTTP
endpoint, http://127.0.0.1:8080 (llama.cpp's llama-server) by default. For hosted
models, point it at OpenRouter with a spend-capped key. See
docs/CONFIG.md.
Repository map
bin/circadia.mjs launcher (source via type stripping in a clone; dist/ when installed)
src/
types.ts shared types
config.ts config defaults, loading, validation
vault/ frontmatter subset, time, wikilinks, fact grammar, note parser, walker
index/ SQLite schema + full-rebuild and incremental indexer
extract/ extraction-scope selection, hipporag triples
retrieval/ keyword (FTS5/BM25), PPR, ACT-R, mode ladder, recall, context rendering
cli/ CLI
mcp/ MCP server (stdio)
serve/ HTTP MCP service, bearer auth, write gate, scheduler (RFC-0003)
consolidation/ episode replay → gate → promote, queue, or supersede
dreams/ REM pass, wake recall, dream edges
eval/ retrieval eval, metrics, ablations, adapters
templates/ Obsidian note templates (copied into vaults by `init`)
examples/vault/ fictional vault exercising every feature
examples/mastra/ Mastra agent using Circadia over MCP
test/ node:test suites
docs/ SCHEMA, ARCHITECTURE, RETRIEVAL, CONFIG, SECURITY, EVAL, ROADMAP, SOURCES,
decisions/, rfcs/, media/
AGENTS.md start here if you are an agent picking this up
Documentation
- docs/SCHEMA.md: the vault format, which is the contract.
- docs/ARCHITECTURE.md: the cognitive principles and each design decision they drive.
- docs/RETRIEVAL.md: modes, scopes, the escalation ladder, scoring, and as-of semantics.
- docs/CONFIG.md: every config key.
- docs/SECURITY.md: threat model and defaults.
- docs/EVAL.md and docs/PERFORMANCE.md: how retrieval is measured, and what the numbers mean.
- docs/ROADMAP.md: phases with acceptance criteria.
- docs/SOURCES.md: every paper, doc, and issue this design draws on.
- docs/LOCAL-SETUP.md: run Circadia as your own agent memory on your machine — vault, index, MCP wiring, and a nightly consolidation timer.
- docs/AGENT-GUIDANCE.md: a drop-in
AGENTS.mdblock that tells an agent to keep its memory in Circadia instead of the harness's own store. - docs/decisions/ and docs/rfcs/: architecture decisions and design proposals.
- AGENTS.md: conventions and invariants for contributors, human or agent. Read it before changing code.
License
Public domain, under The Unlicense.
Установить Circadia в Claude Desktop, Claude Code, Cursor
unyly install circadiaСтавит в Claude Desktop, Claude Code, Cursor и VS Code — сам разбирается с npx, uvx и сборкой из исходников.
Впервые? Поставь CLI: curl -fsSL https://unyly.org/install | sh
Или настроить вручную
Выполни в терминале:
claude mcp add circadia --env CIRCADIA_WRITE_TOKEN_FILE="" -- npx -y circadiaПошаговые гайды: как установить Circadia
FAQ
Circadia MCP бесплатный?
Да, Circadia MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Circadia?
Да, требуются переменные окружения: CIRCADIA_WRITE_TOKEN_FILE. Unyly подставит их в конфиг при установке.
Circadia — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Circadia в Claude Desktop, Claude Code или Cursor?
Открой Circadia на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
Opencode Omniroute Plugin
OpenCode plugin for the OmniRoute AI Gateway. Drives dynamic model discovery, /connect auth flow, and multi-instance OmniRoute providers via the official @openc
автор: GitHub ActionsRoblox Studio
Enables AI coding tools to control Roblox Studio for workspace exploration, instance manipulation, and script management. It provides tools for playtesting, sce
автор: paralovAntigravity Drawio
MCP server that enables AI assistants to create, parse, render, and validate Draw.io diagrams programmatically.
автор: S-SUJAN-SFetch
Web content fetching and conversion for efficient LLM usage.
AWS KB Retrieval
Retrieval from AWS Knowledge Base using Bedrock Agent Runtime.
автор: modelcontextprotocolSpring AI MCP Server
Provides auto-configuration for setting up an MCP server in Spring Boot applications.
llm-analysis-assistant
A very streamlined mcp client that supports calling and monitoring stdio/sse/streamableHttp, and can also view request responses through the /logs page. It also
автор: xuzexin-hzMCP-Agent
A simple, composable framework to build agents using Model Context Protocol by [LastMile AI](https://www.lastmileai.dev)
автор: lastmile-aiSpring AI MCP Client
Provides auto-configuration for MCP client functionality in Spring Boot applications.
mcp.natoma.ai
A Hosted MCP Platform to discover, install, manage and deploy MCP servers by [Natoma Labs](https://www.natoma.ai)
Compare Circadia with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории ai
