Aleph
Architecture

Memory Architecture

Deep dive into Aleph's memory storage, retrieval, and background processing implementation.

This document provides a detailed look at the implementation of Aleph's memory system. For a high-level overview, see Memory System Concepts.


Storage Architecture

Four-Trait Backend Design

The storage layer uses four separate traits rather than a monolithic store. Each caller depends only on the capabilities it needs:

// src/memory/store/mod.rs
pub trait NoteStore: Send + Sync { /* ... */ }
pub trait RawMemoryStore: Send + Sync { /* ... */ }
pub trait DreamStore: Send + Sync { /* ... */ }
pub trait CompressionStore: Send + Sync { /* ... */ }

All four are implemented by SqliteMemoryBackend:

pub struct SqliteMemoryBackend {
    db: Arc<Mutex<Connection>>,
}

pub type MemoryBackend = Arc<SqliteMemoryBackend>;

Schema (SQLite + sqlite-vec)

Core Tables

TablePurpose
raw_memoriesSession transcripts, attachment text, is_processed flag
notes_indexKnowledge note metadata (title, category, tags, hash)
notes_linksBidirectional wikilink graph
notes_ftsFTS5 full-text index over note content
notes_vec_mapLinks (path, agent_id) to numeric rowids
notes_vec_{dim}sqlite-vec virtual tables (768/1024/1536 dims)
memory_eventsImmutable event log (event-sourced mutations)
context_anchorsFact-to-session linkage for provenance
dream_statusDaily/weekly dream run tracking
daily_insightsDream daemon output cache

sqlite-vec Virtual Tables

-- Created per embedding dimension
CREATE VIRTUAL TABLE notes_vec_768 USING vec0(
    embedding float[768]
);
CREATE VIRTUAL TABLE notes_vec_1024 USING vec0(
    embedding float[1024]
);
CREATE VIRTUAL TABLE notes_vec_1536 USING vec0(
    embedding float[1536]
);

The notes_vec_map table bridges human-readable (path, agent_id) pairs to numeric rowids required by vec0 tables.


Retrieval Pipeline

NoteFactRetrieval

The primary retrieval interface for knowledge notes:

// src/memory/note_retrieval/mod.rs
#[async_trait]
pub trait NoteFactRetrieval: Send + Sync {
    async fn retrieve(
        &self,
        query: &str,
        agent_id: &str,
        limit: usize,
    ) -> Result<Vec<ScoredFact<MemoryFact>>, AlephError>;
}

Hybrid Search (Vector + FTS)

Query
  ├── Vector Search (sqlite-vec) → candidates with distance scores
  ├── FTS Search (FTS5) → candidates with match scores
  └── RRF Fusion → unified ranked list

Reciprocal Rank Fusion (RRF) formula:

RRF_score = Σ 1 / (k + rank_i)

where k = 60 (constant), rank_i = rank from source i.

Scoring Pipeline (6 Stages)

StageDescriptionDefault
cosine_rerankBlend vector-search score with fresh cosine similarityenabled
recency_boostAdditive boost for recently created facts+0.1
length_normalizationPenalize very long contentenabled
time_decayExponential decay by age, floor 0.5half-life 30 days
hard_min_scoreDrop candidates below threshold0.35
mmr_diversityMaximal Marginal Relevance — defer near-duplicates to tailλ = 0.5

Deleted stages: importance_weight (removed in Sovereignty Cleanup).


Background Processing

Compression Service

Runs in real-time during idle moments:

Raw Memories (is_processed = false)
  → SessionCompactor: per-session compaction
  → NoteIndexer: create/update knowledge notes
  → mark is_processed = true

Dream Daemon

Runs during system idle, with two cadences:

Daily Pipeline (5 stages):

[Consolidate] → [Drift] → [Lint] → [Decay] → [DailyDigest]

Weekly Pipeline (6 stages):

[Consolidate] → [Drift] → [Synthesis] → [Lint] → [Decay] → [DailyDigest]
// src/memory/dreaming/mod.rs
pub fn ensure_dream_daemon();
pub fn record_activity();  // Call on user activity to reset idle timer

Memory Hub (Panel Topology)

The desktop Memory mode (/memory) is a single host that unifies the two former memory surfaces into one view:

ViewWasRenders
Graph viewradial "canvas" at /memorywikilink graph; nodes are knowledge notes
Table viewfaceted "vault" at /dashboard/memoryfaceted, paginated note list with drawer

Both views share one host and one toolbar (a Graph ↔ Table toggle, a unified search box, and a shared agent selector). The toggle is a CSS view switch over a single mounted host — not two separate routes — so state (search query, selected agent, scroll/zoom) persists across the flip.

  • Node identity == note path. A graph node and a table row that point to the same note share the same stable identity, which is what makes cross-view navigation deterministic.
  • Bidirectional navigation:
    • A table row's View in graph highlights the corresponding node in the graph.
    • A graph node's View in list jumps the table to that note's facet/page and opens its drawer.
  • Routing: the old /dashboard/memory path now redirects to /memory?view=table, preserving any in-flight links and bookmarks.

Governance Surfaces (Read-Only Observability)

Two read-only panels under Settings → Memory surface the memory system's self-governance lifecycle. They are observability only — all writes remain LLM/tool-driven; these surfaces never mutate memory.

Dream Insights

Backed by dreaming.list_insights, this panel reads the DreamStore (the daily_insights cache and dream_status run tracking) to show recent daily synthesis notes produced by the Dream Daemon plus dream-run history (which cadence ran, when, and its status). A companion dreaming.run_now triggers an on-demand dream run.

dreaming.list_insights  →  recent daily synthesis notes + dream-run history
dreaming.run_now        →  trigger an on-demand dream run

Corrections

Backed by memory.list_corrections, this panel surfaces the correction → distillation lifecycle: user corrections captured during conversation and how they were distilled back into knowledge notes. It exposes what the system learned from corrections without granting any way to edit memory directly.


Retrieval Trace

memory.retrieve_with_trace is wired to the real NoteFactRetrieval scoring pipeline and returns per-stage telemetry for debugging why a fact did or did not surface. Instrumentation is collected through a TraceSink that records a StageTrace at each stage:

query
  → candidate pool
  → BM25 + vector scores
  → RRF fusion
  → cutoff
  → ranked

Each StageTrace carries the candidates entering/leaving that stage and their scores, so a caller can see exactly where a fact was boosted, demoted, or dropped. This is a debugging/inspection RPC; it does not change retrieval behaviour — it observes the same pipeline that production retrieval runs.


Working Memory Assembly

HybridAssembler (Spec 2)

Replaces the legacy ContextComptroller::arbitrate path. Produces a MemoryEnvelope with explicit slots:

// src/memory/assembler/mod.rs
pub struct MemoryEnvelope {
    pub system_slots: Vec<MemorySlot>,      // Always-injected facts
    pub user_slots: Vec<MemorySlot>,        // User-query relevant facts
    pub scratchpad: Option<String>,         // Working notes
}

pub struct MemorySlot {
    pub content: String,
    pub source: String,
    pub score: f32,
}
#[async_trait]
pub trait WorkingMemoryAssembler: Send + Sync {
    async fn assemble(
        &self,
        query: &str,
        agent_id: &str,
        session_id: Option<&str>,
        budget: AssemblyBudget,
    ) -> Result<MemoryEnvelope, AlephError>;
}

Assembly Budget

pub struct AssemblyBudget {
    pub max_tokens: usize,      // Target token budget for assembled memory
    pub min_score: f32,         // Minimum fact score threshold
    pub max_facts: usize,       // Maximum number of facts to include
}

Curated Hot Memory (Spec A)

A manually-curated, frozen snapshot of critical facts that bypasses normal retrieval:

// src/memory/curated/mod.rs
pub struct CuratedHotMemory {
    pub facts: Vec<CuratedFact>,
    pub frozen_at: i64,
}

pub struct CuratedFact {
    pub content: String,
    pub priority: u8,        // 1-10, higher = more important
    pub source: String,
}
  • Created via the remember builtin tool
  • Stored in ~/.aleph/memory/curated/{agent_id}.json
  • Injected into every prompt unconditionally (up to token budget)
  • Frozen snapshot — changes require explicit re-curation

Memory Extensions (Spec 4)

Pluggable extensions for custom memory behaviors:

// src/memory/extensions/mod.rs
#[async_trait]
pub trait MemoryExtension: Send + Sync {
    fn name(&self) -> &str;
    
    async fn on_memory_created(
        &self,
        event: &MemoryEvent,
        backend: &MemoryBackend,
    ) -> Result<(), AlephError>;
    
    async fn on_memory_retrieved(
        &self,
        query: &str,
        facts: &[MemoryFact],
        backend: &MemoryBackend,
    ) -> Result<Vec<MemoryFact>, AlephError>;
    
    async fn on_session_end(
        &self,
        session_id: &str,
        backend: &MemoryBackend,
    ) -> Result<(), AlephError>;
}

pub struct ExtensionRegistry {
    extensions: Vec<Box<dyn MemoryExtension>>,
}

Lifecycle hooks:

  • on_memory_created — Triggered after note creation/update
  • on_memory_retrieved — Can modify/filter retrieved facts
  • on_session_end — Cleanup or summarization

Memory Reflector (Spec 2)

A synthesis layer on top of HybridAssembler:

// src/memory/reflector/mod.rs
pub struct MemoryReflector {
    assembler: Arc<dyn WorkingMemoryAssembler>,
    llm: Arc<dyn LlmBackend>,
}

impl MemoryReflector {
    pub async fn reflect(
        &self,
        query: &str,
        agent_id: &str,
    ) -> Result<ReflectionResult, AlephError>;
}

pub struct ReflectionResult {
    pub answer: String,
    pub sources: Vec<MemorySource>,
    pub confidence: f32,
}

Exposed via the memory_reflect builtin tool. Returns a coherent LLM-synthesised answer with cited sources.


Event Sourcing

Every note mutation is captured as an immutable MemoryEvent:

// src/memory/events/mod.rs
pub struct MemoryEvent {
    pub id: i64,
    pub event_type: MemoryEventType,
    pub note_path: String,
    pub timestamp: i64,
    pub payload: String,  // JSON
}

pub enum MemoryEventType {
    NoteCreated,
    NoteUpdated,
    NoteDeleted,
    LinkAdded,
    LinkRemoved,
    TagChanged,
}

Events enable:

  • Audit trail for all memory changes
  • Time-travel queries
  • Conflict resolution
  • Extension hook triggers

Namespace Isolation

Memory namespaces provide scoped storage:

// src/memory/namespace/mod.rs
pub struct MemoryNamespace {
    pub id: String,
    pub parent: Option<String>,
    pub isolation_level: IsolationLevel,
}

pub enum IsolationLevel {
    Full,       // Completely isolated
    Inherited,  // Can read parent, writes isolated
    Shared,     // Read-write shared with parent
}

Safety Properties

ConcernMitigation
UTF-8 truncationchars().take(n) (never mid-character)
Lock poisoningunwrap_or_else(|e| e.into_inner())
SQL injectionParameterized queries via rusqlite
Vector boundsCosine clamped to [-1.0, 1.0]
Token overflowAssemblyBudget enforces limits

Deleted Components (Sovereignty Cleanup)

The following were removed on 2026-04-12:

ComponentReplacement
MemoryTier enumStructural distinction (raw vs notes)
MemoryFact.strengthRemoved (LLM decides relevance)
MemoryFact.confidenceRemoved (LLM decides relevance)
ValueEstimatorDeleted entirely
importance_weight scoringDeleted

Rationale: LLM sovereignty (R8) — deterministic heuristics for "importance" cannot outperform the LLM's own judgment about what matters in context.


Module File Map

PathContents
src/memory/mod.rsModule entry, re-exports
src/memory/assembler/mod.rsWorkingMemoryAssembler trait, HybridAssembler impl
src/memory/curated/mod.rsCuratedHotMemory, remember tool integration
src/memory/extensions/mod.rsMemoryExtension trait, ExtensionRegistry
src/memory/reflector/mod.rsMemoryReflector, ReflectionResult
src/memory/note_retrieval/mod.rsNoteFactRetrieval trait, hybrid search impl
src/memory/notes/note.rsKnowledgeNote struct
src/memory/notes/store.rsNoteStore trait
src/memory/notes/indexer.rsNoteIndexer (Markdown → SQLite)
src/memory/store/mod.rsRawMemoryStore, DreamStore, CompressionStore traits
src/memory/store/sqlite/mod.rsSqliteMemoryBackend impl
src/memory/store/sqlite/vec.rssqlite-vec integration
src/memory/retrieval/mod.rsGeneric retrieval interfaces
src/memory/scoring_pipeline/mod.rs6-stage scoring
src/memory/dreaming/mod.rsDreamDaemon, pipeline stages
src/memory/events/mod.rsMemoryEvent, event sourcing
src/memory/transcript_indexer/mod.rsTranscript chunking and indexing
src/memory/namespace/mod.rsMemoryNamespace, isolation levels
src/memory/context_comptroller/mod.rsLegacy budget manager (deprecated)

On this page