The physical trace of a memory.
A database designed for minds, not tables.
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.
You store embeddings. You search by geometric proximity. Still fundamentally a query against static, passive data — the structure has no opinion about your context.
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.
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.
strength = parent × edge_weight × salience × cos_sim(query, target)
Multiplicative — every factor must be non-trivial for the path to survive
Nodes migrate between tiers based on salience and reinforcement — exactly as memories migrate between the hippocampus and neocortex through activation and sleep.
The K most recently activated nodes. Ultra-fast access. Evicted by recency when capacity is exceeded. What you're actively thinking about right now.
Time-stamped events and raw experiences. What happened, and when. The raw feed of observations before they have been abstracted into knowledge.
The concept graph with weighted associations. Long-term structural knowledge. What you know, abstracted from any specific event that taught it to you.
Encoded patterns, workflows, and repeatable processes. How to do things. Retrieved by similarity to current task context, not by conscious recall.
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.
"Forgetting is not failure. It is the system prioritising what matters."
In the brain, memories consolidate during sleep — the hippocampus replays experiences into the neocortex until they become stable knowledge. Engram makes this explicit.
Raw events. Time-stamped experience. Not yet abstract.
Stable concept. Integrated knowledge. No longer tied to a specific event.
Three retrieval primitives layered over a single embedded store. No daemon. No network. No server to babysit.
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.
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.
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.
A C FFI layer exposes the full API. Language bindings ship for Kotlin, TypeScript, Go, and Rust natively.
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);
}
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}")
}
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}`);
}
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)
}
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.