Crux
GuidesMemory

Memory Stores

How memory blocks use DataStore for persistence, recall, and production storage.

Memory does not define its own storage system. Every memory block persists through DataStore.

That means the same data store adapter can back memory records, indexing metadata, plans, flows, blackboards, and workspace metadata.

import { memory, workingState } from '@crux/core/memory'
import { inMemoryDataStore } from '@crux/core/storage'

const store = inMemoryDataStore()

const mem = memory({
  id: 'assistant',
  namespace: 'thread:123',
  store,
  blocks: [workingState({ id: 'scratch', schema })],
})

For general store APIs, TTL, adapters, subscriptions, and custom database implementations, use the DataStore guide. For vector search, use the VectorStore guide. For workspace files and generated binary outputs, use BlobStore.

What Memory Stores

Memory blocks write JSON records to the configured DataStore.

const mem = memory({
  id: 'support-agent',
  namespace: `user:${userId}`,
  store,
  blocks: [workingState({ id: 'profile', schema: profileSchema }), episodes({ id: 'history', embed: dense })],
})

The id identifies the memory collection. The namespace scopes records to a user, thread, tenant, session, or agent. Use stable namespaces so memory can be recalled on later turns.

Defaults

If you do not pass a store, memory uses in-memory storage. That is useful for tests and local demos, but it is not durable.

const mem = memory({
  id: 'assistant',
  namespace: 'demo',
  blocks: [workingState({ id: 'scratch', schema })],
})

For production, pass a durable DataStore. Convex apps use cruxConvexStore(); see the Convex guide for setup and boundary rules.

Vector Recall

Some memory blocks can use vector search when configured with a vector-capable store path.

const history = episodes({
  id: 'history',
  embed: dense,
})

Rules:

  1. If the store supports vectors, embedding-backed blocks can recall semantically relevant entries.
  2. If the store does not support vectors, blocks that can degrade safely fall back to non-vector behavior such as recency or listing.
  3. Stores should throw for unsupported sparse or hybrid query modes instead of silently degrading.

For dense, sparse, and hybrid vector capabilities, see VectorStore.

TTL And Cache-Like Memory

Memory records can use stores that support TTL. TTL is a store feature, not a memory-only feature.

await store.set(
  'memory:temporary-note',
  {
    content: 'Remember this only briefly.',
  },
  { ttl: 300_000 },
)

Use TTL for short-lived scratch records, resolver caches, or temporary agent state. Do not use TTL for facts, episodes, or user preferences that should survive future sessions.

Reactive Memory UI

Memory records can be rendered reactively through the same transport layer as plans and tasks.

Use:

  • Convex transport for Convex apps.
  • SSE transport when the store implements subscribe().
  • Polling transport for universal fallback.
import { CruxProvider, createPollingTransport, useWorkingMemory } from '@crux/react'

const transport = createPollingTransport(store)

function MemoryPanel() {
  const profile = useWorkingMemory<{ tone: string }>('profile')
  return <pre>{JSON.stringify(profile, null, 2)}</pre>
}

;<CruxProvider transport={transport}>
  <MemoryPanel />
</CruxProvider>

See React reference for exact hook and transport APIs.

Production Choices

For memory, choose storage based on the behavior you need:

NeedUse
Tests and examplesinMemoryDataStore()
Convex app state and reactivitycruxConvexStore() as a DataStore; see Convex
Redis-backed key-value recordscruxRedisStore() as a DataStore
Dense/sparse/hybrid vector recallPair memory data with a VectorStore such as Upstash Vector
Workspace files and generated outputsBlobStore, not memory storage

On this page