Skip to content

Botmem — High-Level Design (HLD)

1. System Overview

Botmem is a local-first personal memory RAG system that ingests events from multiple data sources (emails, messages, photos, locations), normalizes them into a unified memory schema, and provides cross-modal retrieval with weighted ranking.

mermaid
graph TB
    subgraph "Data Sources"
        Gmail["Gmail<br/>(OAuth2)"]
        Slack["Slack<br/>(OAuth2/Token)"]
        WA["WhatsApp<br/>(QR Auth)"]
        iMsg["iMessage<br/>(Local)"]
        Photos["Photos/Immich<br/>(Local)"]
        Telegram["Telegram<br/>(API Key)"]
        Locations["OwnTracks<br/>(HTTP)"]
    end

    subgraph "Ingestion Layer"
        ConnRegistry["Connector Registry"]
        SyncProc["Sync Processor"]
        RawEvents["Raw Events Store"]
    end

    subgraph "Processing Pipeline"
        CleanProc["Clean Processor"]
        EmbedProc["Embed Processor"]
        EnrichProc["Enrich Processor"]
    end

    subgraph "Storage Layer"
        PG["PostgreSQL<br/>(Drizzle ORM)"]
        Redis["Redis<br/>(BullMQ + Cache)"]
        TS["PostgreSQL search index<br/>(Hybrid Search)"]
    end

    subgraph "AI Services"
        Ollama["Ollama<br/>(Local)"]
        OpenRouter["OpenRouter<br/>(Cloud)"]
        Gemini["Gemini<br/>(Embeddings)"]
    end

    subgraph "API Layer"
        NestJS["NestJS 11<br/>REST + WebSocket"]
        AuthGuard["Firebase Auth<br/>+ Recovery Key"]
    end

    subgraph "Clients"
        WebApp["React 19 SPA"]
        CLI["botmem CLI"]
        Agents["AI Agents<br/>(OpenClaw, etc.)"]
    end

    Gmail & Slack & WA & iMsg & Photos & Telegram & Locations --> ConnRegistry
    ConnRegistry --> SyncProc
    SyncProc --> RawEvents
    RawEvents --> CleanProc --> EmbedProc --> EnrichProc
    EmbedProc --> PG
    EmbedProc --> Ollama & OpenRouter & Gemini
    EnrichProc --> TS
    EnrichProc --> PG
    SyncProc --> Redis
    NestJS --> PG & Redis & TS
    AuthGuard --> NestJS
    WebApp & CLI & Agents --> NestJS

2. Architecture Style

AspectChoiceRationale
PatternModular monolith (NestJS modules)Single deployable, clear module boundaries, future-ready for extraction
Data flowEvent-driven pipeline (BullMQ)Decoupled stages, retry semantics, backpressure handling
SearchHybrid RAG (BM25 + vector)Best-of-both-worlds: keyword precision + semantic recall
AuthFirebase Auth + client-side encryptionZero-knowledge encryption, portable identity
DeploymentDocker Compose on VPSSelf-hosted, privacy-first, single-machine simplicity

3. Key Design Decisions

3.1 Store Everything, Label Confidence

Memories are never deleted. Each carries a factuality label (FACT, UNVERIFIED, FICTION) with confidence scores. The ranking formula weights trust and factuality into results.

3.2 Connector-Agnostic Pipeline

All connectors emit ConnectorDataEvent objects. The pipeline doesn't know or care about the source — it normalizes everything into the same Memory schema.

3.3 Per-User Encryption (Recovery Key)

  • User gets a 32-byte random recovery key at signup (shown once)
  • All PII data encrypted with AES-256-GCM using a key derived from recovery key
  • Server never stores the plaintext key — only SHA-256 hash for verification
  • Key cached in memory + Redis (30-day TTL) for session performance

3.4 Swappable AI Backend

Three AI backends (Ollama, OpenRouter, Gemini) behind a unified interface. Switching is a single env var change. LLM responses cached by SHA-256 hash to avoid redundant API calls.


4. Component Architecture

mermaid
graph LR
    subgraph "apps/api (NestJS 11)"
        direction TB
        ConfigMod["Config Module"]
        DbMod["DB Module<br/>(Drizzle + PG)"]
        AuthMod["Auth Module<br/>(Firebase/JWT)"]
        CryptoMod["Crypto Module<br/>(AES-256-GCM)"]
        ConnMod["Connectors Module"]
        AcctMod["Accounts Module"]
        JobsMod["Jobs Module"]
        MemMod["Memory Module<br/>(Search + Pipeline)"]
        PeopleMod["People Module"]
        EventsMod["Events Module<br/>(WebSocket)"]
        SettingsMod["Settings Module"]
        AnalyticsMod["Analytics Module<br/>(PostHog)"]
        OAuthMod["OAuth Module"]
        MemBankMod["Memory Banks Module"]
        PluginsMod["Plugins Module"]
    end

    subgraph "apps/web (React 19)"
        direction TB
        Pages["Pages<br/>(18 routes)"]
        Stores["Zustand Stores<br/>(8 stores)"]
        Components["Components<br/>(Connectors, Memory,<br/>Auth, UI)"]
        Hooks["Custom Hooks"]
    end

    subgraph "packages/"
        direction TB
        SDK["connector-sdk<br/>(BaseConnector)"]
        Shared["shared<br/>(Types)"]
        CLIPkg["cli<br/>(botmem)"]
        Connectors["connectors/<br/>(7 connectors)"]
    end

5. Data Flow Overview

5.1 Ingestion Pipeline

mermaid
flowchart LR
    A["Connector.sync()"] -->|"ConnectorDataEvent"| B["Raw Events<br/>(immutable)"]
    B -->|"sync queue"| C["SyncProcessor"]
    C -->|"clean queue"| D["CleanProcessor<br/>(normalize text)"]
    D -->|"embed queue"| E["EmbedProcessor<br/>(Memory + embedding<br/>+ contact resolution)"]
    E -->|"enrich queue"| F["EnrichProcessor<br/>(entities, claims,<br/>factuality, importance)"]
    F --> G["PostgreSQL search index<br/>(searchable)"]
    E --> H["PostgreSQL<br/>(Memory record)"]

5.2 Query Pipeline

mermaid
flowchart LR
    Q["User Query"] --> NLQ["NLQ Parser<br/>(temporal, entities,<br/>intent)"]
    NLQ --> EMB["Embed Query<br/>(AI backend)"]
    EMB --> TS["PostgreSQL search index<br/>Hybrid Search<br/>(BM25 + vector)"]
    TS --> RANK["Weighted Ranking<br/>(semantic 40%,<br/>recency 25%,<br/>importance 20%,<br/>trust 15%)"]
    RANK --> DEC["Decrypt Results"]
    DEC --> RES["Ranked Results<br/>+ Facets"]

6. Infrastructure

mermaid
graph TB
    subgraph "Production (VPS — 65.20.85.57)"
        Caddy["Caddy<br/>(Reverse Proxy + SSL)"]
        API["NestJS API<br/>(Docker)"]
        PG["PostgreSQL<br/>(Docker)"]
        Redis["Redis<br/>(Docker, AOF)"]
        TSProd["PostgreSQL search index<br/>(Docker)"]
    end

    subgraph "CI/CD"
        GH["GitHub Actions"]
        GHCR["GitHub Container<br/>Registry"]
    end

    subgraph "External Services"
        Firebase["Firebase Auth"]
        PostHog["PostHog Analytics"]
    end

    Internet["Internet<br/>(botmem.xyz)"] --> Caddy
    Caddy --> API
    API --> PG & Redis & TSProd
    API --> Firebase & PostHog
    GH -->|"build + push"| GHCR
    GH -->|"SSH deploy"| API

7. Security Architecture

LayerMechanism
TransportTLS via Caddy auto-SSL
AuthenticationFirebase Auth (email/password) + JWT tokens
AuthorizationPer-user data isolation (account ownership)
Encryption at restAES-256-GCM per-user key (recovery key derived)
Credential storageEncrypted auth context in accounts + connectorCredentials
Secret managementAPP_SECRET env var for server-side encryption
NetworkTailscale VPN for SSH (public SSH disabled)

8. Non-Functional Requirements

RequirementTargetImplementation
PrivacyZero-knowledge encryptionPer-user recovery key, server never sees plaintext key
ScalabilitySingle-user, multi-connectorBullMQ queues with configurable concurrency
ReliabilityJob retry with backoff3 attempts, exponential backoff (5s base), 300s lock
Search qualityHybrid RAGBM25 + vector + weighted scoring
ExtensibilityPlugin connectorsBaseConnector SDK, directory-based loading
ObservabilityReal-time pipeline statusWebSocket events, PostHog analytics, structured logging

Your memories. Your agents. Your control.