DEV Community

Cover image for Agent Memory as Source Code: A DSL for Neurons, Synapses and a Hash-Chained Ledger
Bartosz OSA
Bartosz OSA

Posted on

Agent Memory as Source Code: A DSL for Neurons, Synapses and a Hash-Chained Ledger

Every agent framework has "memory". In practice that usually means a vector store and a retrieval call. You can't see why something was recalled, you can't diff it, and you can't prove nobody changed it.

I wanted memory that works like source code: written in a language, type-checked, compiled, executed deterministically, and audited. So I built NEUROSA-HB, a small DSL and runtime for describing an agent's "brain" as neurons and synapses.

Give agents a brain, not just a context window.

Repo: github.com/HazEOskA/neurosa-human-brain


A brain is a .nsa file

brain OsaBrain {
  region ProjectMemory {
    neuron BrainArchitecture {
      type: decision
      title: "NEUROSA-HB Architecture"
      source: obsidian("projects/brain.md")
      threshold: 0.72
      restingPotential: -0.65
      salience: 0.90
      confidence: 0.95
    }

    neuron HydraLab {
      type: system
      source: obsidian("projects/hydra-lab.md")
      threshold: 0.61
    }

    synapse BrainArchitecture -> HydraLab {
      relation: PART_OF
      mode: EXCITATORY
      weight: 0.64
      confidence: 0.90
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

A neuron is a unit of knowledge: a decision, a system, a fact, backed by a source document. A synapse is a typed, weighted relation between two neurons, and it can be excitatory or inhibitory. Inhibition is the part most memory systems lack: some knowledge should actively suppress other knowledge.


It's a real compiler pipeline

.nsa → lexer → parser → AST → semantic analysis → type checking → IR 0.1 → runtime → activation → SQLite → hash-chain ledger
Enter fullscreen mode Exit fullscreen mode

Every stage is its own package in the monorepo (neurosa-lexer, neurosa-parser, neurosa-type-checker, neurosa-ir, neurosa-activation, neurosa-event-ledger, ...).

Because it's a compiler, memory errors become compile errors. Point a synapse at a neuron that doesn't exist and give it an out-of-range weight:

NEUROSA-E105: Unknown target neuron 'GhostNode'
  at bad.nsa:19:34
NEUROSA-E309: Property 'weight' must be in range 0..1; got 1.7
  at bad.nsa:24:15
Enter fullscreen mode Exit fullscreen mode

(The CLI speaks Polish, my native language. The commands also accept English aliases (parse, check, compile, run), and I've translated the messages above.)

A valid file compiles to a stable JSON IR in which every default is explicit:

{
  "id": "HydraLab",
  "regionId": "ProjectMemory",
  "type": "SYSTEM",
  "threshold": 0.61,
  "restingPotential": 0,
  "salience": 0.5,
  "confidence": 1,
  "enabled": true
}
Enter fullscreen mode Exit fullscreen mode

Recall is activation, not similarity

To "remember", you inject an impulse into a neuron and let it propagate:

node --import tsx packages/neurosa-cli/src/cli.ts run \
  examples/minimal-brain/brain.nsa \
  --stan .neurosa/brain.db \
  --neuron BrainArchitecture \
  --sila 1 \
  --maks-skoki 8 \
  --limit-zdarzen 500 \
  --limit-czasu-ms 5000 \
  --deterministycznie
Enter fullscreen mode Exit fullscreen mode

(The flags are Polish: --stan = state file, --sila = initial strength, --maks-skoki = max hops, --limit-zdarzen = event limit, --limit-czasu-ms = time limit, --deterministycznie = deterministic.)

The core of the activation loop:

const polarity = impulse.mode === "INHIBITORY" ? -1 : 1;
const neuronModulation = neuron.confidence * (0.5 + neuron.salience * 0.5);
const delta = impulse.strength * polarity * neuronModulation;
neuron.activationLevel += delta;

if (neuron.activationLevel < neuron.threshold) continue;   // didn't fire

// fired → propagate along outgoing synapses
const strength = impulse.strength * synapse.weight * synapse.confidence;
Enter fullscreen mode Exit fullscreen mode

So the answer to "why did the agent recall X?" is a trace you can read: which impulse arrived, through which synapse, at what strength, on which hop, and whether the neuron fired or was inhibited.

Every activation runs inside hard limits: maximum hops, minimum impulse strength, maximum events, a time limit, cycle detection and cancellation. Impulses travel only through synapses that exist in the compiled IR, so the runtime can't invent a connection.


Tamper-evident memory

State and events are stored in SQLite (WAL, foreign keys, explicit migrations). Next to them is an append-only, SHA-256 hash-chained ledger. verifyLedger() detects:

  • a modified event payload
  • a deleted or reordered event
  • a wrong previousHash or eventHash

For an agent memory this matters. If an agent decided something because of a memory, you can later prove what that memory said at that moment.


Beyond the DSL

  • Native workspace (Checkpoint A): folders, Markdown documents, frontmatter, tags, wikilinks, backlinks, immutable revisions, SQLite FTS5 search. The documents belong to NEUROSA-HB itself, so Obsidian isn't required.
  • One-shot Obsidian import (Checkpoint B): read-only. It copies notes and safe attachments, turns notes into neurons and wikilinks into real synapses, and writes a report plus ledger events. The source vault is never modified.
  • Connect & Ingest: persistent agent sessions, core context + retrieval, atomic write-back into documents and the ledger. Ingest handles Drive, PDF/DOCX/text and conversation exports. HTTP and MCP adapters share one Brain API.

What is not done

To be straight about the current state:

  • Plasticity is declared, not learned. The language accepts and type-checks plasticity: HEBBIAN, but activation doesn't update weights yet. Synapses don't strengthen with use today.
  • Transmission delay is metadata. transmissionDelayMs is carried on events, but propagation isn't scheduled in real time.
  • The "Living Brain" visualization (Checkpoint D) isn't part of the verified build. Its source package still has to be recovered.
  • This is a recovery build. The repo is a functional reconstruction after losing an earlier unpushed workspace, and it doesn't claim identical sources.
  • Requires Node.js 24+ and pnpm. The native SQLite module won't build on older Node.

Why a language?

You can review a language in a pull request. Memory written as .nsa gets diffs, code review, CI type checks and a deterministic replay. An embedding store gives you none of that.

My bet is that agent memory has to be inspectable and provable, not just relevant.

How do you debug why your agent remembered something?

Top comments (0)