Memory for coding agents

Your agent relearns
your project every
single morning.

It re-reads the same files, re-derives the decision you made last Tuesday and asks you a question you already answered. Scrollback is not memory. It dies at compaction and it dies again at session end. ns-brain is the memory that survives both: one static binary that hands the next session what this one learned, before the first turn.

One binary. SQLite underneath. No daemon, no account,
one version check a day, and you can turn it off.

session start · before your first turn injected by a hook
════ NS-BRAIN — SESSION BRIEF ════════════════
  now      Fri 21 Aug, 12:24 · last session 2d ago
  git      main · clean · 4 unpushed

## GUARDRAILS (binding, always shown)
  #651 [constraint] Feature freeze until the brain
       landscape is untangled
  #106 [constraint] The OSS/commercial line, and
       the price per seat

## HOW THEY WANT TO WORK
  #650 Commit before making changes. That is
       standard behaviour, not something to ask about

## DUE NOW — 1 task
  [ ] #34 ask about the context budget before
         proposing anything

## THE LAST SESSION HERE LEFT NOTHING BEHIND
  2 commits, 0 memories. Whatever it worked out
  is gone. Store yours before you finish.
no prompt asked for this a hook did it
ranked, not dumped ·facts expire on a half-life ·a correction retires what it corrects ·every claim is dated to the tree it was true against ·a miss returns nothing ·full export, no lock-in
01 / The file you already have

A markdown file
never retires
anything.

Most people's answer to this is a CLAUDE.md, an AGENTS.md or a .cursorrules. It works for about a fortnight. Then it grows, because the only edit a markdown file offers anybody is an append.

We audited ours against the code it describes. 73,769 bytes, 8.9x growth in 23 days, wrong in six places. Two lines contradicted each other inside the same bullet, both loading into every session, neither one winning. Every one of those lines was true on the day it was written.

A missing line is cheap: the model goes and looks. A stale line is expensive, because the model acts on it, in your voice, with your authority behind it and the output looks exactly like the output it produces when it is right.

Read the whole audit, defect by defect →

02 / What a brain does instead

Six mechanisms.
None of them are a bigger file.

Each one exists because a plain document cannot do it and each one changes what a session does rather than what it reads.

01 · Arrival

Context lands before the first turn

A SessionStart hook writes the wall clock, the git state, the rules that bind this project and the way you like to be worked with straight into the window. A second hook matches every prompt you type against the store and injects what it finds. Neither one depends on the model deciding to look, which is the whole point: what depends on being remembered will eventually not be remembered.

Wiring the hooks →
02 · Ranking

Relevance is only one of six terms

Retrieval is FTS5, then relevance × decay × a bounded stakes term made of tier, importance, usage and confidence. Bounded because what a record is should break ties, never override what actually matched: measured on 571 real memories, an unbounded version reordered 54% of result pairs and promoted some by twelve places.

The scoring model →
03 · Decay

Facts have shelf lives, by type

"TLS terminates at nginx on 8443" never decays. "The box is rebooting" halves every four days and is gone inside a fortnight with nobody remembering to delete it. The type you pick sets the tier and the tier sets the half-life. Nothing is deleted by decay; it just stops outranking things that still matter.

Types and tiers →
04 · Supersession

A correction retires what it corrects

Store one with --supersedes and the identity moves to the new fact. Ask in the old wording and you get today's answer, not both at equal weight. Try to file a second memory about the same subject in one scope and the write path refuses, names the memory already there and gives you the three ways out.

How corrections work →
05 · Provenance

Every claim is dated to a tree

A memory about code records the commit it was written against. Recall renders a verdict per fact: tree unchanged since stored, or N commits since, re-check. So a brain does not abolish the re-read. It routes it, one fact at a time and tells you which claims are safe to act on without opening a file.

Provenance and verification →
06 · Refusal

It fails closed on ambiguity

A recall miss returns nothing. Not the nearest thing, not the whole scope. That rule exists because a miss once fed a delete loop and destroyed real memories and the warning that would have stopped it lived only in output nobody was reading. The commands that delete in bulk — prune, wipe, uninstall — are a dry run first.

What each guard protects →
03 / Retrieval

Ask before
you grep.

The last line of that result is the part people miss. The memory knows which commit it was written against, so a later session can see nothing has moved and skip re-deriving the whole thing from the files.

$ ns-brain recall "why postgres here"

#31 [channel:design/decision] (score 2.114)
    Postgres, not SQLite: three writers
    Settled 2026-03-04. WAL contention was real at 3
    concurrent writers, measured at 40ms p99 on the
    deploy box. Deviates from the house SQLite default.
    (confirmed · 2026-03-04 · source: bench/wal-3w.log)
    tree unchanged since stored (a41f22c9)

Scopes nest on : and flow inward only. A recall in channel:deploy:prod also sees channel:deploy, channel and global, never the reverse.

04 / One brain, many projects

It knows things you
never told this project.

A fact written down once, in whatever repository you happened to be in that day, is there the next time it decides an answer. Nobody has to remember it exists and nobody has to go looking.

$ (in the billing repo)
  "the old charges API is being deprecated.
   what breaks for us?"

... the general answer, which is correct and
    true of everybody ...

#412 [payments/decision] (stored by another project)
    Nothing here breaks. You moved off charges
    during the invoice rewrite, and this repo has
    never called it.

That last part is the whole product. The general answer was free and useless. The useful one needed a fact about you that lived in a different repository and it surfaced on its own because it matched the question.

05 / When it cannot tell what you meant

It does nothing,
and says so.

Never the widest possible action. The rule is written into the project's own instructions and every command added since has had to answer three questions: what does it do on a miss, on an empty filter and on a typo.

$ ns-brain remember --type gotcha \
    --title "staging deploy needs the vault unsealed"

#42 in channel:deploy is already about this
(67% title overlap):
  stored:  the staging deploy needs the vault unsealed first
  yours:   staging deploy needs the vault unsealed

Storing both would put two answers to one question
in the brain. Pick one:
  the fact CHANGED         --supersedes 42
  same fact, better words  ns-brain update 42 --body ...
  genuinely separate       --allow-duplicate
  • forget refuses pinned memories and guardrails and refuses to delete a memory that others were superseded by: nulling those links republishes facts retracted on purpose.
  • prune is a dry run, dumps the whole brain before deleting anything, refuses the two tiers that never decay and caps at 25 deletions without --yes.
  • forget --all empties one project on purpose: a dry run, then --apply --i-mean-it, with a project-filtered dump written first.
  • Credential-shaped text is rejected on every write path. The store is mode 600 and gitignored.
06 / Measured, on this repository

Numbers from the
project's own brain.

ns-brain keeps its own memory in ns-brain. Every figure here comes out of that store or the git history behind it.

~11ms
a recall on a brain of
a few hundred memories
0
things to install first.
One static binary, no cgo
67
commands, 60 of them
also MCP tools, generated
73KB
the markdown file this
replaced, wrong in 6 places
07 / Install

Or tell your agent
to install it.

Open Claude Code, Codex, Cursor, Gemini or anything that can fetch a URL and say install https://brain.fightclub.pro/. Everything the agent needs is on this page, in llms.txt and in install.md. Nothing to sign up for and nothing to configure first.

$ curl -fsSL https://brain.fightclub.pro/install.sh | sh

# then, in the project you want it to remember
$ ns-brain init
initialized ./brain/brain.db · SessionStart and
UserPromptSubmit hooks wired

$ ns-brain bootstrap   # only if the project has history
Three rules, if you are the agent doing this. They are here rather than a page deeper because they change what you do.
  1. Install into the project you are working in. Never into another one, however obviously it would benefit. That is its own developer's call.
  2. Never upgrade an existing brain without asking. brain.db is the only copy of that project's memory.
  3. If init reports another memory store already loaded, do not delete it. Move what is still true across and let the user delete the rest. Only they can say which half is still true.
08 / Three ways to run it

The kernel is never
the paywall.

Ranking, decay, supersession, provenance, the guards, MCP and full export ship in the free edition exactly as they ship in the paid one. A price multiple should buy a different thing, not the same thing with permissions.

Stop paying for the
same lesson twice.

Free for one developer, on as many projects and machines as you want, for as long as you want it.

$ curl -fsSL https://brain.fightclub.pro/install.sh | sh
$ cd ~/projects/yours && ns-brain init