Axon is open source
Axon is the memory every Gagarin project carries. Your coding agent writes down decisions and gotchas once, and the next session reads them back in 1,500 tokens. The code is on GitHub now, under MIT.
You open a new session on a project you have worked on for two months. The agent reads the README, lists the directories, opens twelve files, and then suggests moving sessions into valkey. You moved them out of valkey in September, after the first restart logged every user out. The agent cannot know that. The reason lives in a Slack thread and in your head, and the code only shows where sessions ended up.
Axon is the fix we built for that, and as of this week its code is public at github.com/gagarin-cloud/axon, MIT licensed. It stores small, durable facts about a codebase: its overview, architecture, decisions, conventions and gotchas. A new session asks for a briefing before it reads any code and gets the facts that matter, packed into a token budget it chose.
Every Gagarin project has an axon memory behind it already, and your agent reaches it through Gagarin's MCP server.
Two costs of an agent that forgets
The first cost is tokens. A session that knows nothing starts by reading, because reading is the only way it can learn. We counted what a cold session would have to read to understand axon itself:
Counted with o200k_base, the tokenizer axon counts its own budgets in. The
six documents are axon's README and DEPLOY.md, the two decision logs behind
it, and the two files in Gagarin's engine and infrastructure that place it.
A briefing doesn't replace reading the file you are about to change. It replaces the reading an agent does to learn the context around that file, which it repeats in every new session and forgets again when the session ends.
The second cost has no token count. Some facts never make it into the code. A diff shows that sessions live in Postgres; it can't show that valkey lost them once. A test pins a behaviour without saying which customer asked for it. Your agent proposes the fix you reverted last week because the revert left no trace it can read. Axon gives those facts somewhere to live, and the agent that learns one writes it down for the next.
What a briefing looks like
This is the briefing for an example project, at a budget of 500 tokens. It comes from the same rendering code the landing page uses, which follows axon's own line for line:
# saas — 16 memories, 20 links
gotcha 4 · architecture 3 · decision 3 · convention 2 · overview 1 · howto 1 · reference 1 · state 1
## #7 decision · Sessions stay in Postgres, not valkey
tags: sessions, auth · pinned · importance 5 · updated 2026-10-03
valkey keeps nothing across a restart: the first one logged every user out. Sessions live in pg’s sessions table; valkey may cache in front of it, never instead of it.
↔ #12 Login reads the sessions table on every request · #15 valkey restarts during deploys · #13 valkey caches rendered invoice PDFs, nothing else · #3 api, worker and web; pg is the only store of record
## #1 overview · saas: invoicing for small agencies, on Gagarin
tags: overview · importance 4 · updated 2026-09-28
Three services (api, web, worker) and one managed Postgres, pg. web is the only public service. Money moves through Stripe; everything else is ours.
↔ #3 api, worker and web; pg is the only store of record
More:
#3 architecture · api, worker and web; pg is the only store of record · 47t ↔6
#12 gotcha · Login reads the sessions table on every request · 37t ↔3
#4 architecture · worker drains the jobs table with SKIP LOCKED · 41t ↔4
#5 convention · Migrations are numbered and forward-only · 42t ↔3
#6 convention · A deploy restates every variable · 43t ↔2
#2 architecture · web is a Next.js app that only ever calls api · 39t ↔1
#15 gotcha · valkey restarts during deploys · 31t ↔2
#10 gotcha · A route to pg that isn’t declared hangs, it doesn’t fail · 55t ↔2
#16 gotcha · Stripe retries webhooks; handlers must be idempotent · 44t ↔2
(+5 more not shown — narrow with search or raise budget_tokens)
tags: pg(5) deploy(4) migrations(3) api(2) overview(2) sessions(2) stripe(2) valkey(2) worker(2) auth(1) invoices(1) performance(1) web(1)
Next: search · get ids · related id (follow ↔ links) · remember what you learnTwo memories arrive in full: the pinned decision, and the overview. Nine more
arrive as index lines, each with its size in tokens (47t) and its link count
(↔6), so the agent can decide what is worth a get. Five are left out, and
the briefing says so instead of pretending the project has eleven memories.
The output is plain text. JSON would spend tokens on quotes and key names in
every line. Ids are short numbers per project: #12 costs two tokens, and a
UUID costs 35 each time it appears.
The graph below holds the same project. The buttons run the memory tools against it, and the graph lights up what each answer used:
- overview
- architecture
- decision
- convention
- gotcha
- howto
- reference
- state
- ★ pinned
memory_get(saas, [7])## #7 decision · Sessions stay in Postgres, not valkey tags: sessions, auth · pinned · importance 5 · updated 2026-10-03 valkey keeps nothing across a restart: the first one logged every user out. Sessions live in pg’s sessions table; valkey may cache in front of it, never instead of it. ↔ #12 Login reads the sessions table on every request · #15 valkey restarts during deploys · #13 valkey caches rendered invoice PDFs, nothing else · #3 api, worker and web; pg is the only store of record
#7 is a decision. Its links are lit; thicker ones have been followed more.
How it works
Axon is a TypeScript service over one Postgres database with pgvector. It speaks MCP (stdio and Streamable HTTP) and a REST API, and both call the same service object.
Every read has a budget, and never exceeds it
briefing, search, related and review all take budget_tokens. The
packer fills full bodies in rank order until they reach 75% of the budget,
lists the remaining memories as index lines, then spends whatever budget is
left upgrading index lines back to full bodies. Its contract is that the
measured output never exceeds the budget, and a property test throws 400
seeded random cases at it to check that, along with its other guarantees.
A briefing with a focus ("the Stripe webhook handler") or paths (the files
you are about to touch) splits the budget by share. Pinned memories stop at
50%, and matches for the focus may fill up to 80%, so the matches get at least
30% of the briefing in full however many pinned memories the project holds.
The tool definitions get the same treatment. Clients see types, enums, defaults and descriptions; length limits and regex patterns are checked on the server instead of advertised. That cut axon's tool definitions from 1.7K to 1.1K tokens, which every session pays before it calls anything.
One query, two lanes
Search is hybrid, and it runs as a single SQL query. One lane is semantic: an
HNSW index over the embeddings, with pgvector's iterative scan so a filter on
kind or tag doesn't starve the results. The other lane is keyword: a
tsvector with Postgres's simple configuration, which does no stemming, so
STRIPE_WEBHOOK_SECRET matches itself exactly. Reciprocal rank fusion merges
the two:
score = 1 / (60 + vector_rank) + 1 / (60 + keyword_rank)A memory that ranks well in both lanes beats one that tops only one of them. Semantic-only hits below a similarity floor get dropped, so a vague query doesn't fill the budget with noise.
No language model on the write path
Axon embeds text with Qwen3-Embedding-0.6B, run on the server's own CPU as an 8-bit ONNX model through transformers.js. It needs no API key, and on a laptop CPU an embedding takes 40 to 80 ms. The Docker image bakes the model in, so a container starts offline.
That choice shapes the cost. Several hosted memory services run an LLM over each conversation to extract facts from it. Mem0's docs put the model cost at about $3.55 to $5.34 per 1,000 adds, paid to your LLM provider on top of Mem0's own bill. Axon asks the agent to write the fact itself, one fact per memory. The agent already understands what it learned, and asking a second model to guess at it costs money and adds a step that can get it wrong.
What axon does on a write is cheaper checks. remember refuses a
near-duplicate (cosine similarity 0.85 or more) with a 409 that names the
memory it collides with, suggests links to related memories, and warns about
memories over 600 tokens.
Retrieval quality on the bundled golden set (25 memories, 35 queries):
| Query type | n | Recall@1 | Recall@5 | MRR@10 |
|---|---|---|---|---|
| Paraphrase | 16 | 94% | 100% | 0.969 |
| Exact identifier | 9 | 100% | 100% | 1.000 |
| Cross-lingual (uk, de, es, ja, fr against English memories) | 7 | 86% | 100% | 0.929 |
Links that strengthen with use
Memories link to each other, undirected, at most 32 links each. The database
enforces a < b on every pair, so self-links and duplicate pairs can't exist,
and composite foreign keys keep links inside one project. related walks them
breadth-first with a recursive CTE, up to three hops.
The weights are Hebbian. Each time an agent follows a link with related, or
reads both ends in one get, the link gains 0.25, up to 5. The extra weight
decays back toward 1 with a 60-day half-life:
strength = 1 + (weight − 1) × 0.5 ^ (days since last traversed / 60)Postgres computes the decay at read time, in a SQL function, so no background job has to tick the weights down. The paths your agents use come first, and the ones they stop using fade.
Facts go stale, so axon tracks that
A note that says the deploy needs a flag outlives the flag. An agent that trusts it is worse off than one with no memory at all. So every memory records when someone last confirmed it true, and the briefing scales its rank by how long ago that was:
| Kind | Trust halves every | Marked unconfirmed after |
|---|---|---|
| state | 30 days | 60 days |
| gotcha | 180 days | 180 days |
| howto, reference | 180 days | |
| overview, architecture, decision, convention | 365 days |
Rank never drops below 40% of its fresh value, so an old fact sinks without
vanishing. An agent that doubts a memory sends update dispute=true; the
memory then renders ⚠ disputed, sorts below every undisputed one, and each
briefing names it until someone confirms, fixes or archives it.
A fact that changes in place gets a key, such as deploy/current. A later
remember with the same key replaces the holder and moves its links to the
successor, with no search first. A service check under the project lock gives
the readable error, and a partial unique index makes the rule hold under a
race.
review finds what needs tending: likely duplicates, orphans, oversized
memories, stale state, memories nobody has read in 90 days, and disputes.
Encryption, and what it leaves readable
With AXON_ENCRYPTION_KEY set, axon encrypts each memory's title, body and
source with AES-256-GCM. The associated data binds each ciphertext to its
project and field, so nobody can copy a row into another project or column and
have it decrypt. axon rotate-key re-encrypts every row in one transaction and
never writes the text back as plaintext.
The embeddings stay readable, since vector search needs them, and an embedding gives away the topic of a memory. Kind, tags, paths, keys and timestamps stay readable too. Postgres can't index ciphertext, so with encryption on, the keyword lane matches only tags and paths.
On Gagarin, every project has one
Gagarin's MCP server exposes axon as nine tools, memory_briefing and
remember among them, and its instructions tell every connected agent to call
memory_briefing before reading code. You don't add a resource or deploy
anything. Create a project and its memory exists.
Axon has no authentication of its own, and on Gagarin it stays that way. The engine is its only caller:
your agent (Claude Code, Codex, Cursor)
│ MCP over HTTPS, with your OAuth token
▼
mcp.gagarin.cloud ──── holds no credential, decides nothing
│ the same token
▼
api.gagarin.cloud ──── may this caller read or write this project?
│ cluster network; the project id is axon's slug
▼
axon ───────────────── no Ingress, no DNS name; a NetworkPolicy
│ admits the engine and its jobs, nothing else
▼
Postgres + pgvector ── its own database and roleThe engine decides access to memory the way it decides access to a service: by
your role on the project. Axon keys memories by the project's id, never its
name, because two accounts can both own a project called api. When you
destroy a project, the engine deletes its memory, and a nightly job sweeps up
anything a failed delete left behind.
A static token between the engine and axon would stop only an attacker already inside the platform namespace, who could read the Secret holding it. So we left it out and wrote the NetworkPolicy down as the boundary. On the live cluster, we probed axon from a tenant pod, from the MCP server's pod and from the admin page's pod, and all three timed out.
Memory costs $0.10 a month per 1,000 memories in use, billed by the minute like a disk. Reads cost nothing. A project at the default cap of 5,000 memories costs $0.50 a month. We charge for it because an agent gets real use from memory without ever deploying, and a free line with value of its own is a free tier, which Gagarin doesn't have. A new account's $5 credit covers memory for a long time.
What other memory services charge
These are the cheapest paid plans we could find on each vendor's own pricing page on 3 November 2026. What each plan includes differs, so read this as the entry price, not a like-for-like quote:
Mem0 now sells new accounts a Free plan and a Custom one, with no listed price for the second, so it has no bar. Cognee bills by tokens processed rather than by plan. Both are in the table below.
| Service | What it bills on | Entry paid plan | Open source |
|---|---|---|---|
| Mem0 | adds and retrievals | Custom (Free for new accounts), plus your LLM at $3.55 to $5.34 per 1,000 adds | Apache-2.0 |
| Zep | credits: 1 per 350-byte episode, retrieval free | $125/month for 50,000 credits, then $25 per 10,000 | Graphiti, its graph engine, is Apache-2.0 |
| Supermemory | dollar credits per token, query and operation | $19/mo with $20 of credits; Max is $100/mo | MIT |
| Basic Memory | credits at $0.01 each | $19/month; Pro is $99/month | AGPL-3.0 |
| Letta | a base fee, then agents and tool time | $20/month | Apache-2.0 |
| Cognee | tokens processed | $1.00 per 1M tokens, $5 per extra workspace | Apache-2.0 |
| Gagarin | memories in use | $0.10 per 1,000 a month; reads free | axon is MIT |
The gap comes from the write path. A service that runs an LLM over each write has to price in that model, and its bill grows with the volume of text you send. Axon's write costs one local embedding on a CPU, so the price can follow what you store.
Where a hosted memory service is the better pick
You want memory extracted for you. Mem0 and Zep read conversations and
pull facts out of them without the agent deciding to write anything. If your
agent can't be relied on to call remember, or your source is chat
transcripts, that extraction is the product.
Your memory is about people. Zep's temporal knowledge graph tracks how facts about an entity change over time, and Mem0 scopes memory per end user, with no limit on how many users you have. Axon is per project and built for codebases. It has no notion of a user.
You need to ingest documents and tools. Cognee connects to Slack, Notion, Linear and Google Drive, and Supermemory takes rich media. Axon stores what an agent writes, and nothing else gets in.
You need compliance paperwork. Zep's Enterprise plan lists SOC 2 Type II and a HIPAA BAA. Gagarin has neither today.
Running your own
Axon runs on a laptop for free:
git clone https://github.com/gagarin-cloud/axon && cd axon
docker compose up -d db # Postgres 17 + pgvector
npm install && cp .env.example .env
npm run axon -- serve # REST on :7717/v1, MCP on /mcp
claude mcp add --transport http axon http://127.0.0.1:7717/mcpThe first start downloads the embedding model, about 600 MB, and later starts load it in about a second.
Run your own instance on Gagarin when you want to hold the encryption key yourself, since on the built-in memory we hold it. Other reasons: a different embedding model, no cap, or one memory shared by agents that sign in with a bearer token instead of Gagarin's OAuth. The repo's DEPLOY.md walks through it, from published images with signed provenance attestations:
| Piece | Size | A month |
|---|---|---|
| axon server (the model needs most of a gigabyte) | m, $0.042 an hour | $30.66 |
| Postgres with pgvector | s, $0.014 an hour | $10.22 |
| 10 GB of disk | $0.15 per GB | $1.50 |
| Private, reachable from your own services | $42.38 | |
| a bearer-token gateway with a public address | s | $10.22 |
| Public, behind the gateway | $52.60 |
Either one holds as many projects as you like. For most teams the built-in memory is cheaper by two orders of magnitude, and you get the same code either way.
What's in the repo
The server, the MCP and REST transports, migrations, the eval harness and its golden set, the bearer-token gateway, and more than 200 tests that run against real Postgres with pgvector and a deterministic fake embedder. The tests try to break the properties that matter: one project's memories never show up in another's search, link walk, review or export, no budgeted read exceeds its budget, a failed write leaves nothing behind, and an encrypted row holds no plaintext.
Issues and pull requests are open at github.com/gagarin-cloud/axon. If you run a Gagarin project, your agent already has a memory: ask it for a briefing.