v0.1.0 · Local-first memory substrate

# Engram

The physical trace of a memory.
A database designed for minds, not tables.

Scroll
The Problem

## Every existing database
gets memory wrong

⊞

### Relational

You store rows. You query rows. Storage and retrieval are entirely separate systems. The structure that holds the data knows nothing about how you will need it back.

⊙

### Vector

You store embeddings. You search by geometric proximity. Still fundamentally a query against static, passive data — the structure has no opinion about your context.

◈

### Graph

You store edges. You traverse paths. Still asking questions of a static structure. The graph doesn't activate — you query it from outside, like a stranger reading a map.

The brain doesn't query.
It activates.

Core Mechanism

## Spreading Activation

Click any node to set it as the seed. Hit Activate to watch the pattern propagate. Activation flows outward through weighted edges — attenuating at every hop, pruned when too weak to matter.

Seed
Click a node to select
Speed
Formula `strength = parent × edge_weight × salience × cos_sim(query, target)` Multiplicative — every factor must be non-trivial for the path to survive
Memory Architecture

## The Four Memory Tiers

Nodes migrate between tiers based on salience and reinforcement — exactly as memories migrate between the hippocampus and neocortex through activation and sleep.

🔥
Working
Prefrontal cortex — hot, volatile

The K most recently activated nodes. Ultra-fast access. Evicted by recency when capacity is exceeded. What you're actively thinking about right now.

// Currently active
task: "Implement activation BFS"
context: spreading_activation_node
recent: hebbian_learning_concept
📖
Episodic
Hippocampus — time-ordered experience

Time-stamped events and raw experiences. What happened, and when. The raw feed of observations before they have been abstracted into knowledge.

2026-04-27T14:23Z event:
"Will explained Dharma Registry.
Patterns logged. ISE confirmed."
🧠
Semantic
Neocortex — stable knowledge

The concept graph with weighted associations. Long-term structural knowledge. What you know, abstracted from any specific event that taught it to you.

concept: "spreading_activation"
→ Causes: "long_term_potentiation"
→ Activates: "associative_memory"
salience: 0.82, activations: 47
⚙️
Procedural
Cerebellum — patterns and habits

Encoded patterns, workflows, and repeatable processes. How to do things. Retrieved by similarity to current task context, not by conscious recall.

process: "commit_workflow"
steps: [stage → test → commit
→ push → check_ci]
triggered by: git_context_match
Forgetting as Design

## Salience — the attention filter

Every node has a salience score. It governs whether a node surfaces during retrieval. It decays. It strengthens on activation. Adjust the sliders to see how the three signals combine.

The Salience Formula
salience = importance
salience = × 1 / (1 + days_since)
salience = × ln(activation_count + 1)
importance Explicit weight at creation. Stable over time. You set it once.
recency At activation: 1.0. After 1 day: 0.5. After 6 days: ~0.14. Asymptotic toward zero.
frequency Log-compressed: 0→1 activation matters more than 100→101. Diminishing returns.
Importance 0.85
Days since last activation 2
Activation count 12
Computed Salience
0.00

"Forgetting is not failure. It is the system prioritising what matters."

Memory Lifecycle

## Consolidation

In the brain, memories consolidate during sleep — the hippocampus replays experiences into the neocortex until they become stable knowledge. Engram makes this explicit.

Episodic

Raw events. Time-stamped experience. Not yet abstract.

tier: Episodic
activation_count: 2
salience: 0.41
activation_count ≥ 5
salience ≥ 0.3
→ promote on consolidate()
Semantic

Stable concept. Integrated knowledge. No longer tied to a specific event.

tier: Semantic
activation_count: 7
salience: 0.68
Promotion is earned by use — activated, reinforced, and found relevant repeatedly over time.
System Design

## Architecture

Three retrieval primitives layered over a single embedded store. No daemon. No network. No server to babysit.

[ Your Intelligence ]
↕
EngramDb API
↕
Spreading Activation
Best-first BFS · multiplicative weights · pruning at 0.01
Vector Search
Flat cosine scan · semantic direction filter · secondary signal
Graph Traversal
BFS by relation type · typed edges · max depth
↕
sled — embedded persistent B-tree · bincode serialization · transactional
↕
[ Your disk — no daemon · no network · local-first ]
Why multiplication?

Addition lets many weak signals accumulate into false relevance. Multiplication is conjunctive: a weak edge, a dormant node, or a semantically irrelevant target all kill the path. This is how associative memory actually works.

Why sled?

Local-first. Transactional. No daemon process, no network socket. HNSW indexing layers on top when needed — the graph structure itself is the primary retrieval mechanism.

Why pruning at 0.01?

Small enough to allow long indirect chains when intermediate edges are strong. Raise it to focus retrieval; lower it for more associative drift. The brain's attention filter, made explicit.

Language Bindings

## Use from anywhere

A C FFI layer exposes the full API. Language bindings ship for Kotlin, TypeScript, Go, and Rust natively.

basic.rs

    use engram_core::{EngramDb, Node, Edge, NodeType, MemoryTier, RelationType};

    // Open or create a local database — no server, no daemon
    let db = EngramDb::open(Path::new("/var/lib/agent/memory"))?;

    // Store a concept with a semantic embedding
    let id = db.put_node(Node::new(
        NodeType::Concept,
        embedding,        // Vec<f32> from your LLM
        content,          // Vec<u8> — any payload
        MemoryTier::Semantic,
        0.9,             // importance 0.0–1.0
    ))?;

    // Link to a related concept
    db.put_edge(Edge::new(id, related_id, RelationType::Causes, 0.88))?;

    // Retrieve by spreading activation — not a query, a pattern completion
    let results = db.activate(
        &[id],              // seed nodes
        &query_embedding,  // direction of thought
        3,                 // max hops
        10,                // top-N results
    )?;

    for r in &results {
        println!("strength={:.4} hops={}", r.activation_strength, r.hops);
    }

Memory.kt

    import ai.neuron.engram.*

    // JVM binding via JNI — same embedded storage, no server
    val db = EngramDb.open("/data/user/0/ai.agent/memory")

    // Store an episodic event
    val id = db.putNode(
        Node(
            nodeType = NodeType.EVENT,
            embedding = llm.embed(text),
            content = text.toByteArray(),
            tier = MemoryTier.EPISODIC,
            importance = 0.8f
        )
    )

    // Activate from current context
    val recalled = db.activate(
        seeds = listOf(id),
        queryEmbedding = currentContext.embedding,
        maxDepth = 3,
        limit = 10
    )

    recalled.forEach {
        println("[${it.hops} hops] strength=${it.activationStrength}")
    }

memory.ts

    import { EngramDb, NodeType, MemoryTier, RelationType } from "@neuron/engram";

    // WASM build — runs in Node or browser, fully in-process
    const db = await EngramDb.open("./agent-memory");

    // Store a concept node
    const id = await db.putNode({
      nodeType: NodeType.Concept,
      embedding: await llm.embed(text),
      content: new TextEncoder().encode(text),
      tier: MemoryTier.Semantic,
      importance: 0.85,
    });

    // Spreading activation — pattern completion, not query
    const recalled = await db.activate({
      seeds: [id],
      queryEmbedding: currentContext.embedding,
      maxDepth: 3,
      limit: 10,
    });

    for (const node of recalled) {
      console.log(`strength=${node.activationStrength.toFixed(4)} hops=${node.hops}`);
    }

memory.go

    import (
        engram "github.com/neuron-technologies/engram-go"
    )

    // CGo binding — links the Rust library, zero copies for embeddings
    db, err := engram.Open("/var/lib/agent/memory")
    if err != nil {
        return err
    }
    defer db.Close()

    // Store a node
    id, err := db.PutNode(engram.Node{
        NodeType:   engram.Concept,
        Embedding:  embedding,
        Content:    []byte(text),
        Tier:       engram.Semantic,
        Importance: 0.9,
    })

    // Activate from seed — the graph does the retrieval
    results, err := db.Activate(
        []engram.UUID{id},
        queryEmbedding,
        3,   // maxDepth
        10,  // limit
    )

    for _, r := range results {
        fmt.Printf("strength=%.4f hops=%d\n", r.Strength, r.Hops)
    }

Philosophy

## Why local

Your memory is not a service.
It is you.
It lives on your hardware, under your control.
It does not leave your device.
It does not phone home.
It does not require a network connection
to remember who you are.

🔒
No telemetry
📡
No network required
🗄️
Embedded storage
⚡
No daemon process
Build Status

## Current state

◈
Version
v0.1.0
✓
Build
Zero warnings · Zero errors
◎
Test Suite
38 passing
Public API Surface · engram-core v0.1.0

    impl EngramDb {
        fn open(path: &Path)                                          -> EngramResult<Self>;
        fn put_node(&self, node: Node)                                 -> EngramResult<Uuid>;
        fn get_node(&self, id: Uuid)                                 -> EngramResult<Option<Node>>;
        fn put_edge(&self, edge: Edge)                                -> EngramResult<()>;
        fn activate(&self, seeds: &[Uuid], emb: &[f32], depth: u8, n: usize)  -> EngramResult<Vec<ActivatedNode>>;
        fn search_embedding(&self, emb: &[f32], limit: usize)          -> EngramResult<Vec<ScoredNode>>;
        fn traverse(&self, from: Uuid, rel: Option<RelationType>, depth: u8) -> EngramResult<Vec<Node>>;
        fn touch(&self, id: Uuid)                                    -> EngramResult<()>;
        fn decay(&self, factor: f32)                                  -> EngramResult<usize>;
        fn consolidate(&self, config: &ConsolidationConfig)          -> EngramResult<ConsolidationReport>;
    }