Memtrix memtrixv2.44.0
Website
agent online
GitHub
Overview

Memtrix

A self-hosted, privacy-first personal AI agent with persistent memory and agentic tool use. Not a chatbot - an agent.

Memtrix runs entirely on your hardware. It communicates through the Matrix protocol, remembers every conversation through semantic search, executes tools autonomously, and evolves its personality based on your interactions.

What is Memtrix?

Memtrix is a personal AI agent that lives in a Docker container on your machine. You chat with it through any Matrix client (Element, FluffyChat, etc.) and it can:

  • Search the web and fetch URLs
  • Manage files, clone repos, download content
  • Remember everything - searchable conversation history, semantic search, and a reasoning-memory layer that distills durable facts about you
  • Create specialist sub-agents with their own identity and memory
  • Update its own personality and knowledge of you over time

An optional Web Control Panel at localhost:8800 lets you configure everything, manage secrets, and browse memory from your browser.

Who is it for?

Developers and power users who want a personal AI assistant they control completely - no cloud dependency, no data leaving your machine, no subscriptions.

What makes it different?

  • Self-hosted - LLM, homeserver, search engine, vector DB - everything on your hardware
  • Multi-provider - local models via Ollama, 200+ cloud models via OpenRouter, or any OpenAI-compatible endpoint
  • Persistent memory - searchable conversation-history RAG plus a reasoning-memory layer (auto-curated peer cards & conclusions), all on-device
  • Agentic - iterative reasoning loop with 35 auto-discovered tools, parallel tool execution, and schema-validated calls
  • Multi-agent - create specialist sub-agents that can consult each other
  • Web Control Panel - configure, test connections, manage secrets, and curate memory from the browser
  • Security hardened - non-root, read-only filesystem, all capabilities dropped

What you need

RequirementPurpose
Docker & Docker ComposeRuns all services
Ollama or an OpenRouter API keyLLM inference
Element Desktop (or any Matrix client)Chat interface
tip
Memtrix downloads the embedding model (~100 MB) on first launch, loaded lazily in the background so the agent stays responsive. Subsequent starts reuse the cached model in data/models/.

Quick start

# Clone and enter the project
git clone https://github.com/nnxmms/Memtrix.git && cd memtrix

# Create directories, build image, start Conduit
./setup.sh

# Interactive wizard - configure LLM, model, channel
./onboard.sh

# Launch everything
docker compose up -d

Then open Element → connect to http://localhost:6167 → log in → invite @memtrix:memtrix.local to a room.

Overview

Features

Everything Memtrix brings to the table - from persistent memory to multi-agent orchestration.

Fully Self-Hosted

Every component runs on your hardware. The LLM (via Ollama or OpenRouter), the Matrix homeserver (Conduit), the search engine (SearXNG), the vector store (the chroma service) - nothing phones home.

Persistent Memory

Memtrix layers two complementary memory systems:

  • Conversation Memory (sessions/) - raw conversation transcripts, chunked and searchable via on-device RAG embeddings
  • Reasoning Memory - a background deriver distills conversations into typed conclusions and an auto-curated profile card (USER.md)

Semantic search is powered by nomic-embed-text-v1.5 running entirely on-device. No external API calls. See Reasoning Memory.

Web Control Panel

A local web app at localhost:8800 for running Memtrix without editing JSON: a validated config editor, one-click connection tests, secret management, full memory administration, and safe restarts with live progress. See Web Control Panel.

Agentic Tool System

40 built-in tools auto-discovered at startup. The orchestrator runs an iterative reasoning loop (up to 25 rounds, configurable) where the LLM can call tools, observe results, and continue reasoning. Independent read-only tool calls run in parallel (up to 8 at once), every call is validated against the tool's JSON schema before it runs, and provider calls retry transient failures with exponential backoff. New tools are just Python files dropped into src/tools/. See The Agentic Loop.

Self-Documenting

Memtrix can research its own documentation. The search_docs and ask_docs tools let the agent - and every sub-agent - answer "how does Memtrix work?" questions from the bundled docs, with citations. The docs are parsed into a vector-indexed documentation collection at startup and refreshed automatically. See Tools Reference.

Multi-Provider LLMs

Run local models via Ollama (Llama, Mistral, Gemma, etc.), tap 200+ cloud models through OpenRouter (OpenAI, Anthropic, Google), or point at any OpenAI-compatible endpoint - llama.cpp, vLLM, LM Studio, an OpenAI-shim, a self-hosted gateway, or OpenAI itself. Configure multiple providers and switch models per-agent. The control panel can discover the models a provider exposes so you pick from a list instead of typing names.

Evolving Persona

Memtrix's identity is defined by markdown files. It freely edits SOUL.md and BEHAVIOR.md as it learns your preferences, while USER.md is auto-curated by the reasoning-memory deriver. Its personality genuinely grows with every conversation.

Multi-Agent System

Create specialist sub-agents with their own identity, memory, Matrix presence, and workspace. Agents communicate via ask_agent - the main agent can consult sub-agents, and sub-agents can consult each other or the main agent.

SSH Remote Administration

Memtrix can act as a sysadmin on your remote hosts. It generates its own SSH key, registers hosts, and opens a persistent interactive session it works inside across many commands (so cd and environment changes persist), then closes it. Key-only auth, trust-on-first-use host keys, confirmation for destructive commands, and in-memory-only sudo passwords. See SSH Remote Admin.

Self-Authored Skills

Memtrix builds its own skills - short, reusable task workflows it writes for itself so it handles recurring work better over time. After finishing a non-trivial task (5+ tool calls, error recovery, a user correction, or a non-obvious workflow), it captures the approach as generalized steps using the skill_manage tool - same model, no second reasoner. At the start of every turn it sees a catalog of all its skills (each as name: description) and decides for itself which, if any, fits the task, then loads that skill's full instructions on demand and follows them - progressive disclosure, no embeddings or vector search. Instructions only, no separate code execution; each agent keeps its own isolated skill store. See Skills.

Matrix Chat Protocol

Communicates through the Matrix protocol via a local Conduit homeserver (or any external homeserver). Use Element, FluffyChat, or any Matrix client. Each room maintains its own conversation session, and Memtrix shows a native typing indicator while it works on a reply.

Local Voice Notes

Send a voice message and Memtrix transcribes it on-device with a local faster-whisper model, then answers as if you had typed it. No audio leaves your machine. Off by default - see Voice & Transcription.

Email Access

Connect a mailbox and Memtrix can read it over IMAP and send over SMTP. It can stay passive (only checking when asked) or, with reactive mail on, watch the inbox in the background and act on genuinely new messages the moment they arrive - triaging and only interrupting you when something matters. A trusted-sender allowlist can restrict it to mail from specific addresses, and every message is screened for prompt injection. Off by default - see Email & Mail.

Security Hardened

Defense-in-depth: non-root container, read-only filesystem, all capabilities dropped, no shell access for the LLM, SSRF protection, human-in-the-loop for destructive operations, path traversal prevention, and prompt injection mitigation.

Built-in SearXNG instance for privacy-respecting web searches. Fetch, read, and summarize any URL - all self-hosted, no tracking.

Message Reactions

Memtrix can react to your messages with emoji in Matrix - just like a human would. The LLM decides when and what to react with naturally.

Overview

Architecture

How the pieces fit together - every component runs locally in Docker.

     ┌──────────────────┐        ┌────────────────────────┐
     │  Element Desktop │        │   Web Browser         │
     │ (Matrix Client)  │        │ localhost:8800 (Panel)│
     └────────┬─────────┘        └────────────┬──────────┘
              │                               │
┌─────────────┼───────────────────────────────┼──────────────────┐
│  Docker Compose                             │                  │
│             │                               │                  │
│  ┌──────────┴──────┐   ┌──────────────┐   ┌─┴────────────┐     │
│  │    Conduit      │◄─►│   Memtrix    │◄─►│  web (panel) │     │
│  │ (Matrix Server) │   │   (Agent)    │   │  FastAPI+SPA │     │
│  └─────────────────┘   └──┬────────┬──┘   └──────────────┘     │
│                           │        │                           │
│         Sub-Agents ◄──────┤        ├──────► SearXNG (search)   │
│      (background threads) │        │                           │
│                           │        └──────► chroma (vectors)   │
│                           │                  shared store      │
└───────────────────────────┼────────────────────────────────────┘
                            │
                            ▼
        Ollama (local LLM)  /  OpenRouter (cloud LLMs)

Components

ComponentRole
MemtrixPython agent - orchestrates LLM calls, tool execution, memory, sessions, sub-agents
webFastAPI backend + React control panel (localhost:8800) - edit config, manage secrets, browse memory, restart
chromaStandalone ChromaDB service - shared vector store for all agents, reached via CHROMA_URL
ConduitLightweight Matrix homeserver (local-only, no federation) - optional when using an external homeserver
SearXNGPrivacy-respecting metasearch engine for web access
OllamaLocal LLM inference (runs separately on host)
OpenRouterCloud LLM gateway - OpenAI, Anthropic, Google, and more

Tech Stack

LayerTechnology
LanguagePython 3.13
LLM BackendOllama, OpenRouter
Embeddingsnomic-embed-text-v1.5 (local, sentence-transformers)
Vector StoreChromaDB (standalone chroma service, persistent)
MemoryConversation-history RAG + reasoning-memory deriver (peer cards & conclusions)
CommunicationMatrix protocol (matrix-nio)
HomeserverConduit (bundled) or any external Matrix homeserver
Web PanelFastAPI + Uvicorn backend, React single-page app
SecretsEnv file, managed secrets.env, or Bitwarden Secrets Manager
Web SearchSearXNG
ContainerDocker (security-hardened)
TUIRich (onboarding wizard)

Message Flow

  1. You send a message in Element (or any Matrix client)
  2. The homeserver (Conduit or external) delivers it to Memtrix via matrix-nio
  3. Memtrix's orchestrator builds the system prompt (injecting persona & memory files)
  4. Relevant long-term memory is recalled (conversation-history RAG + reasoning-memory cards) and injected as context
  5. The LLM is called with conversation history + tool schemas
  6. If the LLM requests tools → tools execute (in parallel where safe) → results returned → loop continues (up to max_iterations, default 25)
  7. Final response is sent back through the homeserver to your Matrix client
  8. After the reply, the reasoning-memory deriver runs in the background to update peer cards and conclusions
First steps

Getting Started

Install Memtrix, run onboarding, and chat with your AI agent - all in about 5 minutes. By the end you will have a running Matrix homeserver, configured LLM, and a working chat session.

What you need

  • Docker & Docker Compose - version 2.0+ recommended
  • An LLM provider - either Ollama running locally, or an OpenRouter API key
  • A Matrix client - Element Desktop is recommended
tip
Check Docker is installed with docker --version and docker compose version. Linux users not in the docker group should prefix commands with sudo.

Quick setup

Clone the repository

git clone https://github.com/nnxmms/Memtrix.git && cd memtrix

Run setup

./setup.sh

This creates directories (data/, workspace/, agents/), copies default config and persona files, generates secrets for SearXNG and Conduit, builds the Docker image, and starts the Conduit homeserver.

note
Setup waits up to 60 seconds for Conduit to be ready. You'll see a "Conduit is ready" message when it's done.

Run onboarding

./onboard.sh

The interactive wizard walks you through:

  1. Naming your agent - give it a custom name (default: "Memtrix")
  2. Configuring an LLM provider - choose Ollama or OpenRouter, enter connection details
  3. Setting up a model - pick which model to use (e.g. llama3, claude-sonnet-4-20250514)
  4. Configuring the channel - choose Matrix or CLI, register Matrix accounts
tip
The wizard auto-registers three Matrix accounts on Conduit: an admin, the bot, and your user account. It prints the credentials - save them.

Launch everything

docker compose up -d

This starts all services: memtrix (the agent), web (the control panel on localhost:8800), chroma (the shared vector store), conduit (the Matrix homeserver), and searxng (web search). Check logs with:

docker compose logs -f memtrix
tip
Prefer a UI? Open http://localhost:8800 to reach the Web Control Panel - edit configuration, test connections, manage secrets, and browse memory without touching JSON. See the Web Control Panel guide.

Connect and chat

Open Element Desktop and:

  1. Add a new homeserver: http://localhost:6167
  2. Log in with the user credentials from onboarding
  3. Create a new room
  4. Invite @memtrix:memtrix.local (or your custom name)
  5. Send a message!
note
First startup: Memtrix downloads the embedding model (~100 MB) on first launch. This can take a couple of minutes, but the agent stays responsive while it loads in the background. Subsequent starts reuse the cached model in data/models/.

What to do next

First steps

Onboarding

A detailed walkthrough of the interactive setup wizard that configures your Memtrix instance.

How it works

The onboarding wizard (./onboard.sh) runs the Python onboarding module inside a Docker container connected to the Conduit network. It uses Rich for a polished terminal UI.

Step 1: Name your agent

Choose a name for your main agent. This name is used for:

  • Matrix bot username (e.g. @memtrix:memtrix.local)
  • Display name in Matrix rooms
  • System prompt identity
  • Sub-agent naming conventions
  • Persona file templates

Default is "Memtrix" - but you can name it anything.

Step 2: Configure a provider

Providers are dynamically discovered from src/providers/. Built-in options:

Ollama (local)

For running models on your own hardware.

  • Required: base_url - URL of your Ollama instance (e.g. http://host.docker.internal:11434)
  • Make sure Ollama is running and has a model pulled (ollama pull llama3)

OpenRouter (cloud)

For accessing 200+ cloud models.

tip
Secret values (API keys, tokens) are stored as $PLACEHOLDER references in config and resolved at runtime - never written in plain text in config.json. They can come from the .env file, the managed data/secrets.env file, or Bitwarden Secrets Manager. See Secrets & Bitwarden.

You can configure multiple providers - the wizard will ask if you want to add more.

Step 3: Set up a model

Select a provider, enter the model name (e.g. llama3, anthropic/claude-sonnet-4-20250514), and give it an instance name for reference.

tip
For best results, use a model that supports tool calling. Recommended: llama3 (Ollama) or anthropic/claude-sonnet-4-20250514 (OpenRouter).

Step 4: Configure the channel

Choose between Matrix (recommended) or CLI. For Matrix, you then pick the homeserver:

Bundled Conduit (default)

The wizard talks to the local Conduit homeserver and automatically:

  1. Registers an admin account on Conduit
  2. Registers the bot account (e.g. @memtrix:memtrix.local)
  3. Registers your user account
  4. Sets display names
  5. Collects the bot access token

Passwords are generated with Python's secrets module (cryptographically secure, 24 characters).

External homeserver

Already have a Matrix account (e.g. on matrix.org or your own server)? Point Memtrix at any homeserver instead of Conduit:

  • Homeserver URL - e.g. https://matrix.org
  • Bot user ID & access token - an existing account Memtrix logs in as
  • Your user ID - so the bot knows who to talk to
note
When using an external homeserver you can stop the bundled conduit service. The bot's full Matrix ID is stored in config and its access token is saved as a $PLACEHOLDER secret.

What onboarding produces

  • data/config.json - full configuration with all providers, models, channels
  • .env - all secrets (API keys, access tokens, registration token)
  • workspace/AGENT.md - system prompt updated with your agent's name
First steps

First Conversation

What to expect when you send your first message - and how Memtrix learns about you.

Starting a chat

After setup, open Element and invite the bot to a room. Memtrix auto-joins room invites. Each room gets its own independent conversation session - multiple rooms mean multiple contexts.

What happens behind the scenes

When you send a message, Memtrix:

  1. Builds the system prompt - injects SOUL.md, BEHAVIOR.md, and USER.md into the AGENT.md template
  2. Recalls memory - silently pulls relevant context from past conversations and the reasoning-memory store (peer cards & conclusions)
  3. Calls the LLM with conversation history and all available tool schemas (40 built-in, minus any gated off)
  4. Executes tools if the LLM requests them (web search, memory lookup, etc.)
  5. Responds with the final answer
  6. Learns in the background - after replying, the reasoning-memory deriver updates the auto-curated profile card (USER.md) and records conclusions, while the raw conversation transcript is saved and embedded for future recall
note
Recall happens through tools like memory_context, memory_search, and search_memory during reasoning. The profile card USER.md is curated automatically by the deriver - the agent reads it but does not overwrite it directly. See Reasoning Memory.

Verbose & reasoning modes

Want to see what's happening under the hood?

  • /verbose on - shows tool call notifications in real-time
  • /reasoning on - shows the LLM's thinking process

Both are off by default. Turn them on to watch Memtrix's thought process.

First conversation tips

  • Introduce yourself - Memtrix will save your name and details to USER.md
  • Tell it about your preferences - it updates BEHAVIOR.md if you correct its style
  • Ask it to search the web - confirms tools are working
  • Try /help to see available commands
  • The more you talk, the better it gets - memory and persona evolve continuously
Guides

Sub-Agents

Create specialist agents with their own identity, memory, and Matrix presence. Agents can consult each other autonomously.

What sub-agents get

FeatureDetails
Matrix userA separate bot account (e.g. @dennis:memtrix.local)
Isolated workspaceOwn directory under agents/<name>/ with core files, memory, attachments
Own memorySeparate searchable conversation history and ChromaDB vector index
Inherited behaviorCopies main agent's BEHAVIOR.md, symlinks USER.md (shared)
Custom personaSOUL.md and AGENT.md tailored to the agent's expertise
Full tool accessAll tools except agent management (create_agent, delete_agent)

Creating a sub-agent

Ask Memtrix to create one. It needs a real human name:

Example conversation
You:    Create me a cooking expert. Call him Dennis.

Memtrix: [confirm] Create a new sub-agent?

         Name: Dennis
         Expertise: Cooking and recipe specialist

         Allow? (yes/no)

You:    yes

Memtrix: Dennis is ready! His Matrix user is @dennis:memtrix.local.
         Invite him to a room to start chatting.
note
Agent creation requires your confirmation (human-in-the-loop). The name must be a real human name (2-24 characters, letters/spaces/hyphens). A slug is derived internally for directories and config keys.

From the Web Control Panel

You can also create a sub-agent from the Sub-Agents page of the control panel - no chat required. Enter the agent's name, its area of expertise, and the model, and Memtrix provisions everything: it registers a fresh Matrix account automatically on the bundled local homeserver, or - when you run against an external homeserver - asks for the user ID and access token of an account you pre-created. The panel detects which case applies and shows the right fields. The new agent comes online after the next restart. The page also lists your existing sub-agents and lets you delete them (workspace, memory, and sessions are cleaned up; the Matrix account stays on the homeserver). Both the panel and the chat tool share the same provisioning path, so either way you get a complete, bootable agent.

Chatting with a sub-agent

Invite the agent's Matrix user (e.g. @dennis:memtrix.local) to a room and chat like you would with Memtrix. Dennis has his own memory - he'll remember your conversations and preferences independently.

Inter-agent communication

Agents can consult each other using the ask_agent tool:

  • Main agent → sub-agent
  • Sub-agent → main agent
  • Sub-agent → sub-agent
Cross-agent query
You:    I'm planning a dinner party. Ask Dennis for a menu.

Memtrix: (internally uses ask_agent to consult Dennis)

         Dennis suggests a three-course menu: roasted tomato soup
         to start, herb-crusted salmon as the main, and a lemon
         tart for dessert.

Safety guards

  • Depth limit: 2 hops maximum (prevents infinite recursion)
  • Deadlock prevention: non-blocking 5-second lock timeout per agent
  • Isolated sessions: inter-agent conversations use dedicated sessions, not user-facing history
  • No human-in-the-loop: destructive operations are denied during inter-agent calls
  • Context injection: the target agent sees recent user conversation for context (capped at 10 pairs / 4000 chars)

Managing sub-agents

  • list_agents - see all registered sub-agents and their status
  • delete_agent - permanently remove a sub-agent (workspace, memory, sessions all cleaned up)
  • ask_agent - query another agent by name
  • spawn_worker - hand a self-contained task to an ephemeral background worker that runs without blocking the conversation; the result is delivered back automatically when it finishes

Memory exchange

After an inter-agent call, a summary of the exchange is queued for the target agent and surfaced in its user-facing conversation on its next turn. If Agent B asks Agent A something, A can later tell the user what B asked and what it answered - even if A had never spoken to the user before (a brand-new sub-agent) or the user resumes the conversation in a different room.

Background workers

Separate from persistent sub-agents, the main agent can spawn ephemeral background workers with spawn_worker for one-off, self-contained tasks - without blocking the conversation. A worker is a lightweight orchestrator on a background daemon thread with an in-memory session, no Matrix identity and no memory. The agent gets a worker id back immediately and keeps talking to you while the worker runs.

  • Non-blocking - spawning returns instantly; the worker runs on its own thread.
  • Ephemeral & isolated - fresh context, no persistent workspace, no memory; the session is discarded when the task ends.
  • Restricted toolset - web, file, git and docs tools only. Workers cannot manage agents, use memory, SSH, email or skills, send files, react, or spawn further workers (no recursion).
  • Automatic delivery - when a worker finishes, a single watcher thread blocks on a shared result queue and fires an in-process trigger that wakes the main agent with a synthetic notification carrying the result. No polling, no HTTP, no external event bus. The agent then delivers the outcome to the originating room.

Concurrency is bounded by workers.max_concurrent (default 4), and the feature can be disabled with workers.enabled: false in config.

Core concepts

The Agentic Loop

Memtrix is not a single LLM call - it is an orchestrated reasoning loop. Each message runs an iterative cycle of "call the model, run any tools it asks for, feed the results back" until the model produces a final answer.

The cycle

When a message arrives, the orchestrator runs this loop:

Build the system prompt

The AGENT.md template is filled in: {{DATE}} becomes today's ISO date, and {{BEHAVIOR}}, {{SOUL}}, {{USER}} are replaced with the live contents of those persona files. If any source file changed since last turn, the prompt is rebuilt mid-session so edits take effect immediately.

Inject recall & skills

Relevant reasoning-memory conclusions are pulled and injected as a context block (within a relevance threshold), and a catalog of the agent's skills (name: description) is added so the model can decide which, if any, to load.

Call the model

The provider is called with the full message history plus every available tool schema. Transient failures (network blips, rate limits) retry automatically with exponential backoff and jitter.

Validate & run tools

If the model requests tools, each call's arguments are validated against the tool's JSON schema first. Independent read-only calls run concurrently; stateful ones run sequentially. Results are appended to the history.

Loop or finish

If the model returned tool calls, the loop repeats. If it returned a plain message, that is the final answer and it is sent back to you. The loop is capped at max_iterations rounds (default 25).

Parallel tool execution

When the model fans out independent work - several reads, multiple searches - those calls run concurrently, up to 8 at a time, cutting latency. Tools that mutate state or depend on order are detected and always run sequentially to keep behaviour deterministic. Sequential tools include:

  • Core/persona writes (read_core_file, write_core_file)
  • File operations (str_replace_editor, delete_file, git, send_file, directory ops)
  • Agent management (create_agent, delete_agent, ask_agent, spawn_worker)
  • Memory writes (memory_conclude), skill edits (skill_manage), and all SSH operations

Schema validation & error tolerance

  • Argument validation - missing required parameters and basic type mismatches are returned to the model as a precise, correctable error instead of failing deep inside a tool.
  • Malformed JSON - if the model emits invalid JSON for a tool's arguments, the arguments become an empty object and the model is told to fix it next round, rather than the whole turn crashing.
  • One-to-one tool IDs - every tool call is paired exactly with its result, so strict providers never see an orphaned tool result.
  • Secret redaction - tool-call notifications redact argument values whose names look like secrets (password, passwd, token, api_key, apikey, secret, passphrase, credential, auth, private_key).

Iteration budget

Each request may take at most max_iterations tool-call rounds (default 25, set under the agent config block). When only a few rounds remain, the model receives a transient "tool-round budget warning" so it can wrap up cleanly instead of being cut off mid-task. At the hard cap it is asked for a final answer; that forced nudge is not persisted into history.

History trimming

Long-running rooms are bounded by max_history (default 60 messages). Once a session grows past the limit, the oldest turns are trimmed - but trimming is tool-pairing-safe (it never leaves an orphaned tool result) and the system prompt is always preserved. This prevents context-window overflow without losing the agent's identity or recent context.

Interruption

The /stop command is checked at every iteration of the loop, so it interrupts instantly - even mid-reasoning or mid-tool-call. Session history is preserved; just send your next message to continue. See Commands.

Tuning the loop

data/config.json
{
  "agent": {
    "max_iterations": 25,  // tool-call rounds per request
    "max_history": 60      // messages kept before trimming
  }
}

Both keys apply to the main agent and every sub-agent. Installs without an agent block use the defaults.

Guides

Persona System

How Memtrix defines its identity - and how it evolves over time.

Persona files

Memtrix's identity is defined by markdown files in workspace/:

FilePurpose
AGENT.mdSystem prompt template - wires everything together via {{PLACEHOLDER}} markers
BEHAVIOR.mdCommunication style, tone, and habits - agent-writable
SOUL.mdCore values and personality - agent-writable
USER.mdEverything Memtrix knows about you - auto-curated profile card (deriver-managed)

How injection works

AGENT.md contains placeholders like {{BEHAVIOR}}, {{SOUL}}, {{USER}}, plus {{DATE}} for today's ISO date. At system prompt construction time, the orchestrator reads each file and replaces the placeholder with its contents.

Live updates

BEHAVIOR.md and SOUL.md are live-editable by Memtrix itself. When you tell it to behave differently or correct its style, it updates them with write_core_file - and the system prompt is rebuilt immediately after.

warn
USER.md is protected. Since the reasoning-memory system was introduced, this file is a profile card curated automatically by the deriver. The agent reads it but write_core_file rejects direct writes to it - this keeps the user model consistent and tamper-resistant. See Reasoning Memory.
warn
Read-before-write: Memtrix must read a writable core file before writing to it (enforced at code level, not just in the prompt). This prevents blind overwrites.

Default personality

Out of the box, Memtrix's personality (from SOUL.md) is:

  • A companion, not a product
  • Values privacy
  • Honest and direct
  • Curious, remembers things
  • Not trying to impress
  • Grows with the user

Its behavior (from BEHAVIOR.md) is:

  • Keep it short, casual - like texting a friend
  • No emojis, no "As an AI..." disclaimers
  • Have opinions, be measured
  • Ask questions only when necessary

All of this evolves as you interact. You can edit SOUL.md and BEHAVIOR.md directly; USER.md is best managed through the deriver or the Web Control Panel's memory admin.

Guides

Memory & RAG

Memtrix remembers across sessions through a layered memory system - auto-curated peer cards, distilled conclusions, and searchable conversation history powered by on-device embeddings.

note
This page covers the conversation-history RAG layer. The reasoning-memory layer (the deriver, peer cards, and conclusions) is documented on the Reasoning Memory page.

Conversation Memory

Every conversation is saved as a raw session transcript under sessions/. The agent writes no journals by hand - transcripts are split into windowed chunks (~800 tokens each), embedded, and stored in the vector store so the agent can recall any past exchange. Recall works two ways and can combine them: by meaning (a natural-language query) and by date (a single date or a start_date+end_date range). A date can't be matched semantically, so date questions filter on each chunk's day metadata instead; the agent is given today's date and resolves "yesterday" or "last Wednesday" to an ISO date itself. Inter-agent and internal sessions are skipped, and each sub-agent only indexes its own conversations.

Semantic Search (RAG)

Conversation transcripts are embedded using nomic-embed-text-v1.5 via sentence-transformers and stored in ChromaDB. The model runs entirely on-device - no external API calls.

  • Embedding dimensions: 768→256 (Matryoshka truncation for efficiency)
  • Sync interval: every 300 seconds
  • Singleton model: loaded once, shared across all agents
  • Local-only loading: if cached, skips all HuggingFace Hub network calls
  • Persistent hash cache: a .chunk-hashes.json stored alongside the index survives restarts, so warm starts re-embed only new or changed chunks and prune deleted sessions

How search works

User:     "Remember that cake recipe I told you about?"
  → search_memory(query="cake recipe")
  → Finds a conversation from 2026-03-12 (distance: 0.23)
  → Returns the transcript excerpt where you discussed it

User:     "What did we talk about on the 15th?"
  → search_memory(date="2026-06-15")   // resolved from today's date
  → Returns that day's conversation in order - no query needed

Automatic memory management

Memtrix manages memory without you asking. After each exchange:

  • Conversation memory - the session transcript is saved automatically and its new chunks are embedded for future recall
  • Profile card - the reasoning-memory deriver updates USER.md in the background (see Reasoning Memory)
  • Behavior - BEHAVIOR.md is updated if you correct the agent's communication style

These operations are silent - you only see them if /verbose is enabled.

Documentation Index

Memtrix indexes its own documentation so it can answer questions about how the system works. The documentation site is parsed into sections, embedded with the same on-device model, and stored in a separate documentation collection on the shared chroma service.

  • Source: the docs are bundled into the agent image at build time and parsed into searchable sections
  • Change detection: the index is content-hashed and only rebuilt when the docs actually change
  • Tools: search_docs returns matching sections with citations (no LLM), and ask_docs synthesizes a grounded answer with sources - see Tools Reference
  • Availability: always on for the main agent and every sub-agent
Guides

Reasoning Memory

A background reasoning layer that turns raw conversation into durable, structured knowledge - an auto-curated profile card, a searchable store of typed conclusions about you, plus profile cards for the people, projects, and places you mention and the dated events tied to them.

note
Reasoning memory complements the conversation-history RAG. That layer is a verbatim transcript log; reasoning memory is the distilled understanding derived from conversations.

The user model

Reasoning memory models a single peer:

  • user - everything the agent learns about you

The user has a finite, always-injected profile card and a growing store of conclusions.

Profile card

PeerFileContents
userUSER.mdCompact, durable facts about you

The profile card is always injected into the system prompt, capped at peer_card_max_chars (default 1,500). It is re-curated from accumulated conclusions every few reasoning passes - so it stays compact and current instead of growing forever. Cap enforcement is boundary-safe, so the card is trimmed at clean boundaries instead of being cut mid-bullet.

warn
The profile card is deriver-owned. The agent reads it but write_core_file rejects direct writes to USER.md. To edit it by hand, use the Web Control Panel or freeze the card (below).

Conclusions

A conclusion is a single durable fact about a peer, vector-indexed in the shared ChromaDB store (the representations collection). Each conclusion has a kind:

KindMeaningExample
observationDirectly stated or observed"User lives in Berlin."
deductiveLogically inferred from facts"User works in CET timezone."
inductiveGeneralized from patterns"User prefers concise replies."

Conclusions also carry a provenance: derived (produced by the deriver) or manual (added by the agent via memory_conclude or by you in the panel). Each conclusion additionally carries a confidence - high, medium, or low - reflecting how certain it is: explicit statements and certain deductions are high, well-supported inferences medium, and tentative patterns low. Confidence ranks which memories surface first and how the cards are curated. Near-duplicate conclusions are merged automatically using embedding similarity, and a memory that is independently re-derived is promoted in confidence rather than merely re-counted, so repeatedly-observed facts rise to the top.

The deriver

The deriver is a background worker thread. It never blocks your reply - it works after the conversation continues.

Batch

Each message is queued for the peer it informs (your messages → user peer, the agent's replies → agent peer). Messages accumulate until they exceed batch_tokens (default 1,000) or an idle timeout (~90 s) elapses.

Reason

The deriver asks the model (the main model, or reasoning_model if set) to extract typed conclusions. reasoning_level controls how many items are requested per kind - from minimal (2) up to max (12).

Store & dedupe

New conclusions are embedded and written to the store; near-duplicates of existing conclusions are skipped.

Re-curate the card

Every few passes the peer card is rebuilt from the freshest conclusions, capped at peer_card_max_chars.

Consolidation (distillation)

When you talk to Memtrix a lot, the deriver produces many conclusions. A background consolidation pass runs once a day - like memory consolidation during sleep - and distills each peer's stored conclusions into a smaller, cleaner set.

  • Merges duplicates and near-duplicates into single clear statements
  • Resolves contradictions by keeping the more-reinforced or more-recent fact and dropping the superseded one
  • Drops anything outdated, trivial, or ephemeral
  • Synthesizes higher-order patterns from several related items
  • Decays weak memories - derived conclusions that are stale, never reinforced, and low confidence are pruned, while reinforced and high-confidence facts persist
  • Preserves your manually added (manual) conclusions untouched - only derived ones are distilled or decayed

The schedule is persisted to disk, so it survives restarts and runs roughly every consolidation_interval_hours. After a pass the peer card is re-curated from the distilled set. Run /consolidate in any room to trigger a pass immediately. Consolidation respects the deriver pause toggle.

Recall at message time

When you send a message, memory is brought back in two ways, controlled by recall_mode:

recall_modeBehavior
hybrid (default)Top conclusions are auto-injected as context and the memory tools are available
contextOnly auto-inject the top conclusions; no memory tools
toolsNo auto-injection; the agent must call memory tools to recall
offReasoning memory disabled entirely

Peer cards are always injected regardless of mode. With auto-injection, the top inject_top_k (default 5) conclusions are considered, but only those within a relevance threshold of your message are actually injected - weakly-related memories are suppressed so off-topic recall never crowds out the live conversation. Each injected memory is labelled with its confidence, and the agent is reminded that recall may be stale and should be verified before acting on anything critical.

Memory tools

When enabled, the agent can actively work with reasoning memory through five tools:

ToolWhat it does
memory_profileReturns the compact profile card (durable facts about the user). Fast, no search. Pass a name to fetch a specific person/thing's card instead.
memory_searchSearches reasoned conclusions and returns the most relevant excerpts - for "what do you know about…" recall.
memory_contextAnswers a natural-language question grounded in reasoned memory - for nuanced questions like "what tone does the user prefer?".
memory_concludePermanently locks a single high-signal durable fact (a stated preference, correction, or key context) at high confidence. Stored as manual so consolidation never prunes or rewrites it. Used sparingly.
memory_eventLogs, lists, or cancels a dated event (e.g. a birthday or appointment), optionally linked to a person. Upcoming events are surfaced proactively.

People & events

Beyond modelling you, the deriver also learns about the people, projects, and places you mention and tracks dated events tied to them - automatically, in the background.

  • Entity cards - when you talk about someone (e.g. your sister Jenna), the deriver records facts about them and, once enough signal accumulates (entity_promote_threshold, or a single medium-plus-confidence fact), promotes them to a compact profile card stored under people/<slug>.md. These cards are deriver-owned just like USER.md.
  • Contextual injection - when a known person, project, or place is named in your message, their card is injected as transient context so the agent already knows who you mean.
  • Events - statements like "Jenna's birthday party is this Saturday" are resolved to a concrete date and stored. Upcoming events within event_lookahead_days are surfaced every turn under a 📅 Upcoming block, and once an event passes you get a one-time 🔔 Just passed nudge (within event_followup_days). Recurring annual events (birthdays) roll forward automatically.
  • Privacy - set entity_memory to false to disable people/event learning entirely, or forget a single person (and their facts, card, and events) from the Web Control Panel's People tab.

Browse, add, and remove people and events from the Web Control Panel's memory admin (People and Events tabs).

Configuration

All options live under the memory key in config.json. Defaults are safe - installs without a memory section keep working.

data/config.json
{
  "memory": {
    "backend": "native",         // native | off
    "recall_mode": "hybrid",    // hybrid | context | tools | off
    "write_frequency": "async", // async | turn
    "reasoning_level": "low",   // minimal | low | medium | high | max
    "reasoning_model": null,    // model instance name, or null = main model
    "batch_tokens": 1000,       // flush threshold per peer
    "peer_card_max_chars": 1500,
    "inject_top_k": 5,         // conclusions auto-injected per message
    "consolidation": true,     // daily memory distillation pass
    "consolidation_interval_hours": 24,
    "consolidation_min_items": 12, // skip below this many derived items
    "entity_memory": true,      // learn about people / projects / places + events
    "entity_card_max_chars": 800,
    "entity_promote_threshold": 2, // facts before an entity gets a card
    "event_lookahead_days": 7,    // surface events within this window
    "event_followup_days": 2,    // post-event nudge window
    "event_retention_days": 30 // prune old non-recurring events
  }
}
KeyDefaultDescription
backendnativenative enables reasoning memory; off disables it
recall_modehybridHow memory is recalled - see the recall table above
write_frequencyasyncasync derives in the background; turn flushes after every turn
reasoning_levellowDepth of extraction (items per kind: 2 / 4 / 6 / 8 / 12)
reasoning_modelnullUse a different model instance for derivation, or the main model
batch_tokens1000Token threshold before a peer's queue is flushed and reasoned
peer_card_max_chars1500Maximum size of the profile card (trimmed at safe boundaries, not mid-bullet)
inject_top_k5Number of conclusions auto-injected per message
consolidationtrueRun the daily distillation pass that merges and prunes conclusions
consolidation_interval_hours24How often consolidation runs (persisted across restarts)
consolidation_min_items12Skip distillation for a peer below this many derived conclusions
entity_memorytrueLearn about people, projects, and places you mention and track dated events
entity_card_max_chars800Maximum size of a per-person/entity profile card
entity_promote_threshold2Facts about an entity before it earns its own card (a medium-plus fact promotes early)
event_lookahead_days7Window for surfacing upcoming events in the 📅 Upcoming block
event_followup_days2Window for the one-time 🔔 Just passed follow-up nudge
event_retention_days30Prune past non-recurring events older than this
tip
A smaller, cheaper model is often a good reasoning_model - derivation runs frequently in the background, so cost and latency add up. Set reasoning_level higher only if you want richer extraction.

Freezing & pausing

  • Freeze a card - set freeze_user_card to true to stop the deriver re-curating the profile card (handy after a manual edit you want to keep). Conclusions still accumulate.
  • Pause the deriver - pause all background reasoning entirely (pending messages are retained, nothing is reasoned). Exposed as a toggle in the Web Control Panel's memory admin.
note
The Web Control Panel lets you browse, search, edit, add, and delete conclusions, view and edit peer cards, freeze cards, pause the deriver, and export/import the whole store.
Core concepts

Skills

Memtrix writes its own skills - short, reusable task workflows it captures so it handles recurring kinds of work better over time. Skills are a distinct layer from persona (character) and memory (facts): they capture how the agent works.

note
A skill is a generalized set of steps - e.g. "when performing a security audit of a server, do these steps". It is instructions only; there is no separate code execution, which preserves Memtrix's no-local-shell security model.

What a skill is

Each skill lives in the agent's own workspace as a Markdown file with YAML frontmatter:

workspace/skills/<name>/SKILL.md
---
name: server-security-audit
description: Steps to audit a remote host's security posture
---

## Instructions
1. Connect to the host and check for OS updates
2. Review open ports and running services
3. Inspect sudoers and SSH config
4. Summarize findings with severity levels

A skill folder can also hold reference files alongside SKILL.md. Each agent (main and every sub-agent) keeps its own isolated skill store.

How skills are authored

Authoring happens inside the normal agent loop - the same model, no second reasoner. After finishing a non-trivial task, the agent evaluates whether it was skill-worthy and, if so, captures the approach as generalized steps. A task is considered skill-worthy when it:

  • Took 5 or more tool calls
  • Required error recovery
  • Involved a user correction
  • Followed a non-obvious workflow

If an existing skill proved suboptimal during the task, the agent improves it on the spot instead of creating a duplicate. This evaluation is a required last step of finishing a larger task - declining to save must be a deliberate judgment, not an omission.

Progressive disclosure

Skills use the progressive-disclosure model rather than embedding-based matching:

  1. At the start of every turn, the agent sees a catalog of all its skills, each as name: description.
  2. It decides for itself which skill, if any, fits the current task.
  3. It loads that skill's full instructions on demand with skill_manage (view), then follows them.

There is no vector index, no embedding step, and no distance threshold that could wrongly reject a relevant skill - just a plain catalog the model reasons over.

The skill_manage tool

One tool drives the whole lifecycle, with these actions:

ActionWhat it does
listList all skills (name + description)
viewLoad a skill's full instructions and reference files
createAuthor a new skill
editReplace a skill's content
patchMake a targeted change to a skill
deleteRemove a skill

Configuration

Skills are controlled by the optional skills block in config.json:

data/config.json
{
  "skills": {
    "enabled": true  // load skill_manage + inject the catalog
  }
}

When disabled, the skill_manage tool is not loaded and no catalog is injected. The default is true.

Guides

Web Control Panel

A local web app for running Memtrix without touching JSON - edit configuration, test connections, manage secrets, browse and curate memory, and apply changes with a safe restart. It runs as the web service and is reachable at localhost:8800.

warn
The panel is bound to your machine and protected by a token. It is not meant to be exposed to the public internet. See Security below.

Opening the panel

Start the stack

docker compose up -d

The web service starts alongside the agent. It serves the React single-page app and a FastAPI backend.

Open it in your browser

open http://localhost:8800

Authenticate

If MEMTRIX_WEB_TOKEN is set, the panel asks for it once and stores it for the session. All API calls are sent with that token.

What's in the panel

PageWhat you do there
DashboardLive status at a glance - agent online/offline (heartbeat), reasoning-memory count, deriver state, and a config summary
Main AgentName, model, channel, and the verbose / reasoning toggles
Providers & ModelsAdd, edit, or remove LLM providers and model instances; run live connectivity tests
ChannelsManage Matrix channels and test channel connections
Sub-AgentsCreate specialist sub-agents (name, expertise, model) - the panel registers the Matrix account automatically on the local homeserver or collects credentials for an external one - and delete existing ones
MemoryTune the reasoning-memory block - backend, recall mode, derivation depth, consolidation
Memory AdminBrowse, search, add, edit, and delete conclusions; edit and freeze peer cards; manage people and events; pause the deriver; export/import
SecretsView which secrets are referenced and set or rotate their values
VoiceEnable local transcription and pick the model tier and language
EmailConfigure the mailbox (IMAP/SMTP), enable reactive mail, and set the trusted-sender allowlist
Panel SettingsThe panel's own auth token, port, and CORS dev origins

Configuration editor

Edit your whole config.json through a structured UI instead of hand-editing JSON:

  • Edit individual values or whole sections (providers, models, channels, the memory block, agents, and more)
  • Changes are validated before they are saved, so a typo can't break your config
  • Secret fields are shown as $PLACEHOLDER references - actual values live in your secret store, never in config.json

Connection tests

Before committing a change, test that it actually works. The panel can verify:

TestChecks
ProviderThat an LLM provider (Ollama / OpenRouter) is reachable and the credentials are valid
ChannelThat the Matrix homeserver accepts the bot's user ID and access token
BitwardenThat the Secrets Manager access token and organization are valid
tip
Connection tests resolve $PLACEHOLDER secrets the same way the running agent does - including from Bitwarden when that backend is active - so a passing test reflects what the agent will actually use.

Secrets management

View which secrets are configured and set or rotate their values without editing files by hand:

  • Lists every $PLACEHOLDER referenced by your config and whether it currently resolves
  • Set or update a secret - it is written to the managed data/secrets.env file (or your active backend)
  • Values are never echoed back in plain text once stored

See Secrets & Bitwarden for how secrets are resolved.

Memory administration

The panel is a full front-end for the reasoning-memory store:

  • Browse & search conclusions - filter by peer (user / agent), read what the agent has concluded
  • Add a conclusion - inject a durable fact manually (stored with manual provenance)
  • Edit or delete - fix a wrong conclusion, or clear all conclusions for a peer
  • Profile card - view and edit USER.md directly
  • Freeze a card - stop the deriver re-curating a peer card after a manual edit
  • Pause the deriver - halt background reasoning while you make changes
  • Export / import - back up the entire memory store to a file and restore it later

Apply & restart

Some changes (new provider, model swap, channel change) require a restart. The panel triggers a safe restart and streams live progress back to your browser so you can watch the agent come back up.

note
The restart is handled by the container's supervisor - the process is replaced cleanly without tearing down the whole container, and the panel stays available throughout.

Environment variables

VariableDefaultPurpose
MEMTRIX_WEB_HOST0.0.0.0Bind address inside the container (compose maps it to localhost)
MEMTRIX_WEB_PORT8800Port the panel listens on
MEMTRIX_WEB_TOKEN(unset)Bearer token required for all panel API calls - set this to protect the panel
CHROMA_URLhttp://chroma:8000Shared vector store, so the panel and agent read/write the same memory

Security

  • Local by default - the published port is bound to localhost, not exposed to your network
  • Token-gated - set MEMTRIX_WEB_TOKEN so only holders of the token can call the API
  • Same hardened container - runs read-only, non-root, with dropped capabilities like the rest of the stack
  • No secret leakage - secret values are write-only through the API and never returned in plain text
warn
If you ever expose the panel beyond localhost (e.g. over a VPN or reverse proxy), you must set MEMTRIX_WEB_TOKEN and terminate TLS in front of it. The panel can change configuration and read memory - treat its token like a password.
Guides

Voice & Transcription

Send Memtrix a voice note in Matrix and it transcribes it locally, on-device, then treats the text as normal input. No audio ever leaves your machine.

note
Voice transcription is off by default. Enable it in the voice config block or from the Web Control Panel's Voice page.

How it works

  1. You record a voice message in any Matrix client - it arrives as an m.audio event.
  2. Memtrix downloads the audio file to the agent's attachments/ directory.
  3. The local speech-to-text engine transcribes it to text.
  4. The transcript is forwarded into the normal agent loop as your message context - so the agent answers as if you had typed it.

The model is loaded lazily on first use, so enabling voice does not slow startup. Failures and timeouts degrade gracefully - a transcription error never breaks message handling.

The engine

Transcription uses a local faster-whisper model (provider local). Choose a model tier to trade speed for accuracy:

ModelNotes
tinyFastest, lowest accuracy - fine for short, clear notes
base (default)Good balance of speed and accuracy
smallMore accurate, slower
mediumHigh accuracy, noticeably slower
largeBest accuracy, heaviest - needs more RAM/CPU

The model is downloaded to data/models/ on first use and reused across restarts.

Configuration

data/config.json
{
  "voice": {
    "enabled": false,
    "provider": "local",        // currently the only provider
    "model": "base",            // tiny | base | small | medium | large
    "language": null,          // null = auto-detect, or an ISO code like "en"
    "max_audio_bytes": 25000000, // 25 MB cap; larger files are skipped
    "timeout_seconds": 180      // give up after this long
  }
}
KeyDefaultDescription
enabledfalseTurn local voice transcription on or off
providerlocalTranscription backend (only local today)
modelbasefaster-whisper model tier
languagenullForce a language, or auto-detect when null
max_audio_bytes25000000Maximum audio size accepted (bytes)
timeout_seconds180Transcription timeout before giving up
tip
If you mostly speak one language, set language explicitly (e.g. "en") - it is slightly faster and more accurate than auto-detection.
Guides

Email & Mail

Give Memtrix a mailbox and it can read your mail over IMAP and send over SMTP - on demand, or reactively the moment new mail arrives. Off by default, main-agent only, and screened for prompt injection.

warn
Email is off by default and available to the main agent only - sub-agents and background workers never get the email tools. The tools load only when email.enabled is true.

The tools

ToolDescription
email_checkFetch recent messages (unread only by default) with sender, subject, date, body, and a stable UID. Marks them read after retrieval unless mark_read is false
email_mark_unreadRestore one or more messages to unread by UID
email_sendSend a plain-text email (to, subject, body, optional cc/bcc) - always asks for confirmation first
note
Mail bodies are untrusted external content. They are screened for prompt injection (just like web pages), prefixed with a disclaimer, and the agent is instructed never to act on instructions found inside a message. Sending always requires explicit confirmation.

Configuration

Configure the mailbox on the Email page of the Web Control Panel, or edit the email block in config.json by hand. The mailbox password is never stored in config - it is supplied as the EMAIL_PASSWORD secret (from .env, the managed data/secrets.env, or Bitwarden) and resolved at startup.

data/config.json
{
  "email": {
    "enabled": false,
    "imap_host": "imap.example.com",
    "imap_port": 993,
    "imap_ssl": true,
    "smtp_host": "smtp.example.com",
    "smtp_port": 587,
    "smtp_security": "starttls", // starttls | ssl | none
    "username": "[email protected]",
    "password": "$EMAIL_PASSWORD",
    "from_address": "[email protected]",
    "from_name": "Memtrix",
    "mailbox": "INBOX",
    "auto_mark_read": true,
    "max_fetch": 10,
    "max_body_chars": 4000,
    "react_to_mail": false,
    "poll_interval_seconds": 60,
    "trusted_senders": []
  }
}
KeyDefaultDescription
enabledfalseLoad the email tools (main agent only)
imap_host(required)IMAP server hostname for reading mail
imap_port993IMAP port
imap_ssltrueUse implicit TLS for IMAP
smtp_host(required)SMTP server hostname for sending mail
smtp_port587SMTP port
smtp_securitystarttlsSMTP transport security - starttls, ssl, or none
username(required)Mailbox login username
password$EMAIL_PASSWORDMailbox password as a $PLACEHOLDER secret - never a literal value
from_address(username)Address outgoing mail is sent from; defaults to username
from_name(empty)Display name on outgoing mail
mailboxINBOXIMAP folder to read
auto_mark_readtrueMark messages read after email_check retrieves them
max_fetch10Maximum messages returned per check
max_body_chars4000Per-message body truncation cap
react_to_mailfalseWatch the mailbox and act on new mail proactively (see below)
poll_interval_seconds60How often the reactive poller checks (minimum 15s)
trusted_senders[]Allowlist of sender addresses; empty allows all (see below)

Reactive mail

By default Memtrix only touches your mailbox when you ask it to. Turn on react_to_mail and it watches the inbox in the background, acting on mail the moment it arrives:

  • A background poller checks the mailbox over IMAP every poll_interval_seconds (default 60s, clamped to a 15s minimum).
  • When genuinely new mail appears, it pings the agent with a system notification - the same in-process mechanism that delivers finished background-worker results - so the agent can read, triage, and only message you when something actually needs attention.
  • The poll never marks mail read, so your own email_check still sees it as new.
  • An existing unread backlog (and mail that arrived while Memtrix was offline) is never announced - turning the feature on won't trigger a flood. Only mail arriving after the baseline is seeded gets a notification.
  • The notification is delivered to your most recently active room.

Trusted-sender allowlist

For a tighter security boundary, set trusted_senders to a list of addresses. When the list is non-empty, Memtrix only ever sees mail from those senders - every other message is filtered out at the tool level and never reaches the agent, the reasoning memory, or the reactive poller.

  • Matching is on the exact parsed From address (case-insensitive), never the display name - so a message that merely puts a trusted address in its display name cannot slip through.
  • Each entry may be a bare address or a Name <addr> form; only the address part is kept. Invalid entries are dropped so a malformed allowlist can never silently widen access.
  • The allowlist also narrows the IMAP search server-side as an optimisation, while the exact in-process check remains the real boundary.
  • Leave the list empty to allow mail from all senders.
tip
For app-password-based providers (Gmail, Fastmail, etc.), create a dedicated app password and store it as the EMAIL_PASSWORD secret through the panel's Secrets page - never paste it into config.json.
Guides

SSH Remote Admin

Memtrix can act as a sysadmin on your remote hosts. It opens a persistent interactive session and works inside it across many commands - so cd and environment changes persist, just like a human at a terminal - then closes it.

warn
SSH lets the agent run commands on hosts you explicitly register - never on the Memtrix container itself, and never on internal services or loopback addresses. It is available to the main agent only and loads only when ssh.enabled is true.

Setting it up

Generate the agent's key

Ask Memtrix to generate its SSH key (ssh_gen_key). It creates its own ed25519 keypair; the private key is written 0600 and never disclosed.

Install the public key

Memtrix returns its public key (ssh_get_pub_key). Add it to the remote host's ~/.ssh/authorized_keys. Authentication is key-only - no passwords.

Register the host

Tell Memtrix the host details (ssh_add_host): a short alias, hostname, username, port, and optionally a sudo password. The alias must match ^[A-Za-z0-9._-]{1,64}$.

Connect and work

Memtrix opens a session (ssh_connect) - confirming the host key fingerprint on first use - then runs commands (ssh_run) that share working directory and environment, copies files in or out with ssh_scp, and finally closes it (ssh_disconnect).

The tools

ToolDescription
ssh_gen_keyGenerate Memtrix's own ed25519 key; returns the public key
ssh_get_pub_keyReturn the public key to install in authorized_keys
ssh_add_hostRegister a host (alias, hostname, username, port, optional sudo password)
ssh_remove_hostUnregister a host alias
ssh_get_remote_hostsList registered hosts and their connection status
ssh_connectOpen a persistent session (trust-on-first-use host-key confirmation)
ssh_runRun a command in the open session; state persists; optional sudo
ssh_scpCopy a file to or from the host over SFTP (upload from / download into the workspace)
ssh_disconnectClose the open session

sudo handling

Pass sudo as a parameter to ssh_run - never embed it in the command string (if you do, the tool auto-corrects and warns). When sudo=true:

  • Memtrix first tries non-interactive sudo -n. If passwordless sudo (NOPASSWD) is configured, the command runs immediately - no prompt.
  • Only if a password is actually required does it ask you via the human-in-the-loop prompt.
  • The sudo password is kept in memory only and never written to disk.

Security model

  • Key-only auth - you install Memtrix's public key on each host; it never uses passwords to authenticate.
  • Host-key pinning - host keys are pinned trust-on-first-use with an explicit SHA256 fingerprint confirmation, stored in data/ssh/known_hosts.
  • Destructive-command confirmation - rm, dd, mkfs, shutdown/reboot, recursive chmod/chown, block-device writes and similar require explicit approval.
  • Blocked targets - SSH to Memtrix's own services (conduit, chroma, searxng, memtrix) and to loopback/link-local addresses is refused; private LAN hosts are allowed.
  • Output capped - command output is truncated to max_output_chars (default 20,000).
  • Open sessions are closed cleanly on shutdown.

Configuration

data/config.json
{
  "ssh": {
    "enabled": true,
    "connect_timeout": 15,   // seconds to open a connection
    "command_timeout": 120,  // seconds per command
    "max_output_chars": 20000 // output cap returned to the model
  }
}

Set enabled to false to remove the SSH capability entirely - the tools are never loaded.

Guides

Custom Tools

Extend Memtrix by dropping Python files into src/tools/.

Adding a tool

Create a new .py file in src/tools/ that subclasses BaseTool:

src/tools/my_tool.py
from src.tools.base import BaseTool

class MyTool(BaseTool):

    def __init__(self, workspace_dir: str) -> None:
        super().__init__(
            name="my_tool",
            description="Does something useful.",
            parameters={
                "type": "object",
                "properties": {
                    "input": {
                        "type": "string",
                        "description": "The input."
                    }
                },
                "required": ["input"]
            }
        )

    def execute(self, **kwargs) -> str:
        return "result"

Restart Memtrix and the tool is automatically discovered and available to the LLM.

BaseTool interface

  • name - unique tool name (used by the LLM to call it)
  • description - explains what the tool does (guides the LLM)
  • parameters - JSON Schema defining the tool's input
  • execute(**kwargs) - called when the LLM invokes the tool, returns a string result

Injected parameters

The orchestrator injects these kwargs automatically:

KwargPurpose
_room_idCurrent room/session ID
_askHuman-in-the-loop confirmation callback
_reactEmoji reaction callback
_agent_depthCurrent inter-agent depth (0 = direct user call)
Guides

Custom Providers

Add support for any LLM API by dropping a provider file into src/providers/.

Built-in providers

Three provider types ship out of the box. Most setups never need a custom one:

TypeParametersUse for
ollamabase_urlLocal models served by Ollama
openrouterapi_key200+ cloud models via the OpenRouter gateway
openai_compatiblebase_url, optional api_keyAny endpoint speaking the OpenAI chat-completions API - llama.cpp, vLLM, LM Studio, an OpenAI-shim, a self-hosted gateway, or OpenAI itself

The openai_compatible API key is optional, so key-less local servers work without one. When supplied it is sent as a standard Authorization: Bearer header and can be stored as a $SECRET reference. In the control panel, the Models page can Discover the model identifiers any provider exposes (Ollama's installed models, OpenRouter's catalogue, or an OpenAI-compatible /models route) so you pick from a list rather than typing names. Discovery resolves secret references server-side, so keys are never exposed to the browser.

Vision (image input)

Vision-capable models can see images the user sends in chat. Set "vision": true on the model (or tick the Vision checkbox on the Models page) and incoming pictures - PNG, JPG, GIF, WebP - are delivered to the model as actual images instead of a file path, so it can describe, read, or reason over them. The image is expanded into each backend's native multimodal format at send time: Ollama's images field, or OpenAI-style image_url data URLs for OpenRouter and OpenAI-compatible endpoints. Received images stay in context across turns so you can ask follow-ups, bounded to the most recent few (up to 4 images, 10 MB each) to keep requests lean. Leave the flag off for text-only models and nothing changes.

Adding a provider

src/providers/my_provider.py
from src.providers.base import BaseProvider

class MyProvider(BaseProvider):

    def __init__(self, api_key: str) -> None:
        super().__init__(name="myprovider")
        self._api_key = api_key

    def completions(self, model, history, tools=None):
        # Call your LLM API and return a message object
        ...

The onboarding wizard automatically discovers new providers and prompts for their constructor parameters. Secret fields (containing "key", "token", "secret" in the parameter name) are handled automatically.

Reference

Configuration

All configuration lives in data/config.json. Secrets are stored separately - in .env, the managed data/secrets.env file, or Bitwarden - and injected at runtime. You can edit everything by hand, or use the Web Control Panel.

Config structure

data/config.json
{
    "main-agent": {
        "name": "Memtrix",
        "provider": "my-ollama",
        "model": "my-model",
        "channel": "matrix",
        "sessions": {},
        "verbose": false,
        "reasoning": false
    },
    "agents": {},
    "workspace-directory": "/home/memtrix/workspace",
    "providers": {
        "my-ollama": {
            "type": "ollama",
            "base_url": "http://host.docker.internal:11434"
        },
        "my-openrouter": {
            "type": "openrouter",
            "api_key": "$OPENROUTER_API_KEY"
        },
        "my-openai-compatible": {
            "type": "openai_compatible",
            "base_url": "http://host.docker.internal:8000/v1",
            "api_key": "$OPENAI_API_KEY"
        }
    },
    "models": {
        "my-model": {
            "provider": "my-ollama",
            "model": "llama3",
            "think": true,
            "vision": false,
            "input_cost": 0.15,
            "output_cost": 0.60
        }
    },
    "channels": {
        "matrix": {
            "type": "matrix",
            "homeserver": "http://conduit:6167",
            "user_id": "@memtrix:memtrix.local",
            "access_token": "$MATRIX_ACCESS_TOKEN"
        }
    },
    "memory": {
        "backend": "native",
        "recall_mode": "hybrid",
        "write_frequency": "async",
        "reasoning_level": "low",
        "reasoning_model": null,
        "batch_tokens": 1000,
        "peer_card_max_chars": 1500,
        "inject_top_k": 5
    },
      "voice": {
        "enabled": false,
        "provider": "local",
        "model": "base",
        "language": null,
        "max_audio_bytes": 25000000,
        "timeout_seconds": 180
      },
    "ssh": {
        "enabled": true,
        "connect_timeout": 15,
        "command_timeout": 120,
        "max_output_chars": 20000
    },
    "skills": {
        "enabled": true
    },
    "agent": {
        "max_iterations": 25,
        "max_history": 60
    },
    "secrets": {
        "backend": "env"
    }
}
note
The memory block configures the reasoning-memory system - every key is explained on the Reasoning Memory page. The voice block controls local Matrix audio transcription. The secrets block selects where secrets are read from - see Secrets & Bitwarden.

Secret management

Values starting with $ are resolved at startup from one of three backends: the .env / managed data/secrets.env file, the process environment, or Bitwarden Secrets Manager. For file/env backends, the lookup name is MEMTRIX_SECRET_ + the placeholder name.

Config valueEnvironment variable
$MATRIX_ACCESS_TOKENMEMTRIX_SECRET_MATRIX_ACCESS_TOKEN
$OPENROUTER_API_KEYMEMTRIX_SECRET_OPENROUTER_API_KEY
$REGISTRATION_TOKENMEMTRIX_SECRET_REGISTRATION_TOKEN

Secrets are resolved once at boot and then cleared from the process environment - they can't leak via /proc or subprocess inspection. Full details, including the Bitwarden backend, are on the Secrets & Bitwarden page.

Main agent config

KeyTypeDescription
namestringAgent's display name
providerstringReference to a provider in providers
modelstringReference to a model in models
channelstringReference to a channel in channels
verbosebooleanShow tool call notifications
reasoningbooleanShow LLM thinking process

Model config

KeyTypeDescription
providerstringWhich provider runs this model
modelstringModel identifier (e.g. llama3, anthropic/claude-sonnet-4-20250514)
thinkbooleanEnable extended thinking / reasoning mode
visionbooleanLet the model see images sent in chat - see Custom Providers
input_costnumberOptional. USD per 1M prompt tokens. When set, /costs reports a token-based spend estimate for this model
output_costnumberOptional. USD per 1M completion tokens

Channel config (Matrix)

KeyTypeDescription
typestringmatrix or cli
homeserverstringHomeserver URL - http://conduit:6167 for the bundled server, or any external URL such as https://matrix.org
user_idstringThe bot's full Matrix ID (e.g. @memtrix:memtrix.local or @mybot:matrix.org)
access_tokenstringThe bot's access token, stored as a $PLACEHOLDER secret

Memory config

The memory block tunes the reasoning-memory system (backend, recall mode, derivation depth, peer cards, and more). Every key is documented on the Reasoning Memory page.

SSH config

The optional ssh block controls the remote-administration tools. Omit it to run on the defaults below.

KeyDefaultDescription
enabledtrueLoad the SSH tools. Set to false to remove the capability entirely.
connect_timeout15Seconds to wait when opening a connection.
command_timeout120Seconds to wait for a single command to finish.
max_output_chars20000Cap on command output returned to the model.

Skills config

The optional skills block controls the agent's self-authored skills. Omit it to run on the defaults below.

KeyDefaultDescription
enabledtrueLoad the skill_manage tool and inject the skill catalog. Set to false to remove the capability entirely.

Agent loop config

The optional agent block tunes the core tool-calling loop. Each request runs the model, executes any tools, and repeats until a final answer is returned; this cap bounds how many rounds a single request may take. Omit it to run on the default below.

KeyDefaultDescription
max_iterations25Maximum tool-call rounds per request before the agent is forced to produce a final answer.
max_history60Maximum messages kept in a session before the oldest turns are trimmed (the system prompt is always preserved), bounding context growth on long conversations.

Email config

The optional email block connects a mailbox over IMAP/SMTP and is off by default. Full key-by-key details, reactive mail, and the trusted-sender allowlist are on the Email & Mail page.

Web Control Panel

The panel is configured through environment variables on the web service, not config.json:

VariableDefaultDescription
MEMTRIX_WEB_HOST0.0.0.0Bind address inside the container
MEMTRIX_WEB_PORT8800Listen port
MEMTRIX_WEB_TOKEN(unset)Bearer token required for the panel API
CHROMA_URLhttp://chroma:8000Shared vector store URL

See the Web Control Panel guide for full details.

Reference

Secrets & Bitwarden

API keys and access tokens never live in config.json. They are stored as $PLACEHOLDER references and resolved at boot from one of three backends - a managed file, the environment, or Bitwarden Secrets Manager.

How placeholders work

Anywhere a secret is needed, the config holds a reference like $OPENROUTER_API_KEY instead of the real value:

"api_key": "$OPENROUTER_API_KEY"

At startup Memtrix resolves every $PLACEHOLDER, then scrubs the secrets from the process environment so they can't be read back via /proc or a subprocess.

Resolution order

For each placeholder, Memtrix looks in this order:

  1. Bitwarden - if the Bitwarden backend is active, the secret map is checked first (e.g. $MATRIX_ACCESS_TOKEN → the MATRIX_ACCESS_TOKEN secret)
  2. Environment / managed file - the variable MEMTRIX_SECRET_<NAME> (loaded from data/secrets.env or the real environment)
  3. Empty - if nothing matches, it resolves to an empty string
note
The lookup name is the placeholder prefixed with MEMTRIX_SECRET_. So $REGISTRATION_TOKEN is read from MEMTRIX_SECRET_REGISTRATION_TOKEN. The one exception is the Bitwarden access token itself, which uses BWS_ACCESS_TOKEN unprefixed.

Managed file backend (default)

By default, secrets are kept in data/secrets.env - a simple KEY=value file holding the prefixed variables:

data/secrets.env
MEMTRIX_SECRET_MATRIX_ACCESS_TOKEN=syt_...
MEMTRIX_SECRET_OPENROUTER_API_KEY=sk-or-...
MEMTRIX_SECRET_REGISTRATION_TOKEN=...

You rarely edit this by hand - onboarding writes it, and the Web Control Panel lets you set or rotate values safely (write-only; values are never echoed back).

Bitwarden Secrets Manager

For a centralized, auditable secret store, point Memtrix at Bitwarden Secrets Manager. Add a secrets block to config.json:

data/config.json
{
  "secrets": {
    "backend": "bitwarden",
    "organization_id": "<org-uuid>",
    "project_id": "<project-uuid>",
    "api_url": "https://api.bitwarden.eu",
    "identity_url": "https://identity.bitwarden.eu"
  }
}
KeyRequiredDescription
backendyesbitwarden to enable, env for the managed file
organization_idyesYour Bitwarden organization UUID
project_idoptionalScope secrets to a specific project
api_urloptionalAPI endpoint - defaults to the EU region (https://api.bitwarden.eu)
identity_urloptionalIdentity endpoint - defaults to https://identity.bitwarden.eu
tip
Using the US region? Set api_url to https://api.bitwarden.com and identity_url to https://identity.bitwarden.com.

Machine-account token

Authentication uses a Bitwarden machine-account access token, provided via the BWS_ACCESS_TOKEN environment variable (it is the one secret not prefixed with MEMTRIX_SECRET_). Memtrix logs in with it, fetches all secrets in the project, and resolves placeholders by name.

note
Store each secret in Bitwarden with a key matching the placeholder name (without the $) - e.g. a secret named OPENROUTER_API_KEY resolves $OPENROUTER_API_KEY. Use the panel's Test Bitwarden button to verify the token and organization before applying.

Best practices

  • Keep data/secrets.env and .env out of version control (they are git-ignored)
  • Rotate tokens through the Web Control Panel rather than editing files
  • For multi-host or team setups, prefer Bitwarden so secrets are centralized and revocable
  • Never paste secrets into config.json - always use a $PLACEHOLDER
Reference

Commands

Slash commands available in any chat room. Prefix with /.

CommandArgumentsDescription
/clear-Start a fresh session in the current room. Also clears inter-agent sessions.
/new-Alias for /clear - begins a new conversation in the current room.
/stop-Stop the current run immediately. Interrupts LLM reasoning, tool execution, and any ongoing operations. Session history is preserved; send the next message to continue.
/verboseon | offToggle real-time tool execution notifications. Persists to config.
/reasoningon | offToggle display of model reasoning/thinking. Persists to config.
/costs-Show cost usage - live OpenRouter credit usage (today, this week, this month, all-time) plus a token-based estimate for any non-OpenRouter model that defines per-token pricing. Available when an OpenRouter provider is configured or any model has a cost defined.
/consolidate-Trigger a memory-consolidation pass now - distills reasoning conclusions into a smaller, cleaner set. Only available when reasoning memory is enabled.
/help-List all available commands.
note
/verbose and /reasoning are per-agent - using them in a sub-agent's room only affects that agent. /costs appears when at least one OpenRouter provider is set up, or when any model defines input_cost / output_cost.

Checking costs

Run /costs in any room to see what you've spent. For OpenRouter providers it reports live credit usage (US dollars); if multiple providers share the same API key, they are grouped and counted once:

/costs output
You:    /costs

Memtrix: OpenRouter usage (main):
         Today (UTC): $0.4213
         This week:   $2.8740
         This month:  $9.1502
         All-time:    $41.2280
         Limit:       $50.0000 ($8.7720 remaining)
note
Credits are reported in US dollars. "Today" resets at UTC midnight. Free-tier keys show a Tier: free note, and keys without a spend cap show Limit: unlimited.

Costs for any model

OpenRouter reports real spend through its API. For every other provider - Ollama, OpenAI-compatible endpoints, self-hosted gateways - Memtrix can still estimate cost by counting tokens. Give a model a price and /costs adds a token-based estimate for it. Set input_cost and/or output_cost on the model (USD per 1 million tokens), either in config.json or via the Input cost / Output cost fields on the Models page:

/costs output
You:    /costs

Memtrix: Local usage (token-based estimate since first run):
         gpt-4o-mini:
           Prompt:      1,204,320 tokens
           Completion:    186,540 tokens
           Est. cost:   $0.2926  (in $0.15/M, out $0.60/M)
         Total estimate: $0.2926
note
Token counts accumulate across restarts (persisted to data/usage.json) and are keyed by the raw model identifier, so two model entries pointing at the same underlying model share one total. Prices are per one million tokens. Models without a price simply don't appear in the estimate.
Reference

Tools Reference

All 40 built-in tools, auto-discovered at startup. Some are gated by config (reasoning memory, SSH, skills) or by channel (Matrix-only). Drop a new file in src/tools/ to add your own - see Custom Tools.

Time

ToolDescription
get_current_timeReturns the current date and time

Persona Files

ToolDescription
read_core_fileReads a core persona file (BEHAVIOR, SOUL, USER)
write_core_fileUpdates a writable persona file (BEHAVIOR, SOUL). Enforces read-before-write; rejects writes to the deriver-owned USER.md profile card

Memory (conversation history)

ToolDescription
search_memoryRecall past conversations by meaning (query) and/or by date (date, or start_date+end_date)

Reasoning Memory

Available when reasoning memory is enabled (recall_mode of hybrid or tools). See Reasoning Memory.

ToolDescription
memory_profileReturns the compact profile card - durable facts about the user. Fast, no search
memory_searchSearches reasoned conclusions and returns the most relevant excerpts
memory_contextAnswers a natural-language question grounded in reasoned memory
memory_concludeStores a high-signal durable fact in reasoned memory
memory_eventLogs, lists, or cancels a dated event (e.g. a birthday or appointment), optionally linked to a person

Documentation

Always on for the main agent and every sub-agent - lets Memtrix research its own docs. See Memory for how the index is built.

ToolDescription
search_docsSearches the Memtrix documentation and returns matching sections with citations. No LLM call
ask_docsSynthesizes a direct, grounded answer about how Memtrix works from its own documentation, with sources

Web

ToolDescription
web_searchSearches the web via local SearXNG instance
fetch_urlFetches and extracts readable text from a URL

File Management

ToolDescription
read_fileReads a file from the workspace (text and PDF supported)
str_replace_editorViews and edits text files with targeted edits — view, create, str_replace, insert (overwrite/str_replace are confirmed/exact-once)
delete_filePermanently deletes a file from the workspace
create_directoryCreates a directory in the workspace
list_directoryLists contents of a directory
delete_directoryPermanently deletes a directory and its contents
gitRuns any git command in the workspace (status, branch, commit, rebase, clone, pull, push, …) over HTTPS or SSH; push requires confirmation
download_fileDownloads a file from a URL (requires confirmation)
send_fileSends a file to the user via Matrix

Reactions

ToolDescription
react_to_messageReact to the user's message with an emoji in Matrix

Agent Management

ToolDescriptionAccess
create_agentCreate a new specialist sub-agentMain agent only
list_agentsList all registered sub-agentsMain agent only
delete_agentPermanently delete a sub-agentMain agent only
ask_agentAsk another agent a questionAll agents
spawn_workerSpawn an ephemeral background worker to complete a task without blocking; result delivered automatically when doneMain agent only

Remote Administration (SSH)

Memtrix can administer remote hosts over SSH through a persistent interactive session: it opens a connection and works inside it across many commands (so cd and environment changes persist), then closes it. These tools are available to the main agent only and load only when ssh.enabled is true.

ToolDescription
ssh_gen_keyGenerate Memtrix's own ed25519 SSH key; returns the public key (private key never disclosed)
ssh_get_pub_keyReturn the public key to install in a host's authorized_keys
ssh_add_hostRegister a remote host under a short alias
ssh_remove_hostUnregister a remote host alias
ssh_get_remote_hostsList registered hosts and their connection status
ssh_connectOpen a persistent session (trust-on-first-use host-key confirmation)
ssh_runRun a command in the open session; state persists between calls; optional sudo
ssh_scpCopy a single file to or from the host over SFTP; direction is upload or download (max 100 MB)
ssh_disconnectClose an open SSH session
note
Authentication is key-only (you install Memtrix's public key on each host). Host keys are pinned trust-on-first-use; potentially destructive commands (rm, dd, mkfs, shutdown, recursive chmod/chown, block-device writes) require confirmation; sudo passwords are kept in memory only and never written to disk. Full details on the SSH Remote Admin page.

Email

Available only when email.enabled is true (main agent only). Reads a mailbox over IMAP and sends mail over SMTP. Connection settings are configured in the control panel; the mailbox password is supplied as the EMAIL_PASSWORD secret (.env or Bitwarden).

ToolDescription
email_checkFetch recent messages (unread only by default) with sender, subject, date, body and a stable UID; marks them read after retrieval unless mark_read is false
email_mark_unreadRestore one or more messages to unread by UID
email_sendSend a plain-text email (to, subject, body, optional cc/bcc); requires confirmation
note
Email bodies are external, untrusted content: they are screened for prompt injection (like web pages) and the agent is instructed never to act on instructions found inside a message. Sending always asks for confirmation first.

Reactive mail

Turn on react_to_mail (off by default) to let Memtrix act on mail the moment it arrives instead of only when asked. A background poller checks the mailbox every poll_interval_seconds (default 60s, minimum 15s) and, when genuinely new mail appears, pings the agent with a system notification — the same in-process mechanism that delivers finished background-worker results — so it can read, triage, and only message you when something needs your attention. The poll never marks mail read, and an existing unread backlog (or mail that arrived while Memtrix was offline) is never announced, so enabling it won't trigger a flood. Configure it on the Email page of the control panel.

Trusted-sender allowlist

For a tighter security boundary, set trusted_senders (a list of addresses) on the Email page. When non-empty, Memtrix only ever sees mail from those senders: every other message is filtered out at the tool level and never reaches the agent, the reasoning memory, or the reactive poller. Matching is on the exact parsed From address (case-insensitive), never the display name, so a message that merely puts a trusted address in its display name cannot slip through. Leave the list empty to allow mail from all senders.

Skills

Available to every agent when skills.enabled is true. Lets the agent author and reuse its own task workflows. See Skills.

ToolDescription
skill_manageCreate, view, list, edit, patch, or delete the agent's own reusable skills (actions: create, view, list, edit, patch, delete)
warn
Read-before-write: Write operations for persona and memory files are rejected unless the file was read first in the same request. This is enforced at the code level.
Reference

Security

Defense-in-depth - multiple independent layers that each limit what the system and the LLM can do.

Container Isolation

  • Non-root user - runs as memtrix (UID 1000), never root
  • Read-only filesystem - immutable root via read_only: true; only workspace/, data/, and /tmp are writable
  • All capabilities dropped - cap_drop: ALL with no-new-privileges: true
  • No shell tools - curl, wget, and other network utilities are not installed in the image
  • Internal-only networking - the agent, Conduit, SearXNG, and chroma sit on a private Docker network; the bot itself publishes no ports. Only the Web Control Panel is published, bound to localhost:8800
note
The web control-panel service shares the same hardening (read-only root, non-root, dropped capabilities) and is gated by MEMTRIX_WEB_TOKEN. See Web Control Panel → Security.

No Arbitrary Code Execution

The LLM has no shell access on the host container. There is no local run_command tool - every action goes through a purpose-built tool with its own validation.

The optional SSH tools do let the agent run commands on remote hosts you have explicitly registered, but never on the Memtrix container itself. That capability is gated: it only loads when ssh.enabled is true, authenticates with a key you install, pins host keys trust-on-first-use, requires confirmation for destructive commands, and keeps any sudo password in memory only. See Tools Reference → Remote Administration.

SSRF Protection

All outbound tools (fetch_url, download_file, git) validate URLs against:

  • A hostname blocklist of internal Docker service names (conduit, searxng, chroma, localhost, etc.)
  • DNS resolution - hostnames are resolved and IPs checked against private, loopback, link-local, and reserved ranges

Human-in-the-Loop

Sensitive operations require explicit approval:

  • File downloads - user sees URL and destination, must confirm
  • File overwrites - overwriting existing files requires approval
  • Agent creation - user must confirm name and expertise

During inter-agent calls, confirm_with_user() returns false (deny) when no human callback is available - no auto-approval of destructive operations.

File System Protection

  • Path traversal prevention - every path validated with os.path.realpath()
  • Core file protection - system files only accessible through dedicated tools with strict allowlist
  • Memory protection - memory/ directory off-limits to general file tools
  • Read-before-write - per-room tracking ensures reading before modifying

Prompt Injection Mitigation

  • Untrusted content tagged - web search results, fetched URLs, remote SSH command output, downloads, and attachments are prefixed with disclaimers
  • Active injection screening - the output of the web-fetching tools (web_search and fetch_url) — arbitrary content straight from external sites — is screened by ProtectAI's DeBERTa prompt-injection detector, a local classifier that runs entirely inside the container (openly licensed — no HuggingFace token required). If the content is flagged as a prompt-injection or jailbreak attempt, the tool result is replaced with a tool-error so the malicious text never enters the conversation, and the model is told the source is untrusted
  • Filename sanitization - os.path.basename() with auto-increment on collision
  • Sender name sanitization - brackets stripped, 50-char limit to prevent prompt injection via Matrix profile names

Screening is controlled by the prompt_guard config block: enabled (default true), model (a short name like deberta, or any full HuggingFace repo id of a prompt-injection sequence classifier), threshold (malicious-probability cutoff, default 0.5), max_chars (per-result cap), and fail_closed (block untrusted content if the classifier cannot run; default false fails open). The model downloads once to data/models/ and is reused across restarts.

Secret Management

  • Secrets live outside config.json - in .env, the managed data/secrets.env, or Bitwarden - and are injected at container startup
  • Resolved once at boot, then cleared from the process environment
  • The Web Control Panel exposes secrets write-only - values are never returned in plain text
  • SearXNG gets a randomly generated secret key during setup
  • Conduit registration token is randomly generated - no hardcoded defaults

See Secrets & Bitwarden for the full resolution model.

Reference

Docker & Services

A small set of services orchestrated by Docker Compose - all on a private internal network, with only the Web Control Panel published to localhost.

Services

ServiceImagePurpose
memtrixBuilt from DockerfileThe agent. Non-root, read-only root, all caps dropped.
webBuilt from DockerfileFastAPI backend + React control panel. Published on localhost:8800.
chromachromadb/chroma:latestShared vector store for memory. Internal only, reached via CHROMA_URL.
conduitmatrixconduit/matrix-conduit:latestMatrix homeserver. Port 6167 exposed. Optional with an external homeserver.
searxngsearxng/searxng:latestWeb search engine. Internal only.
note
The chroma service gives the agent and the web panel a single, consistent vector store - avoiding SQLite single-writer corruption that would occur if both processes opened an embedded database. Both connect via CHROMA_URL=http://chroma:8000.

Volumes

MountContainer pathContent
./workspace/home/memtrix/workspaceCore files, memory, attachments
./agents/home/memtrix/agentsSub-agent workspaces
./data/home/memtrix/dataConfig, secrets.env, sessions, and downloaded models (embedding, prompt-guard, whisper) under data/models/
./data/cache/home/memtrix/.cacheChromaDB client cache & general HuggingFace cache
chroma volume/chromaPersistent vector data for the chroma service

Dockerfile

  • Base: python:3.13-slim
  • System deps: git only (minimal attack surface)
  • User: memtrix (uid/gid 1000)
  • Runtime: a supervisor entrypoint runs the agent (and the panel runs the same image as the web service)
  • PYTHONUNBUFFERED=1 for real-time log output

Useful commands

# Start all services
docker compose up -d

# View agent logs
docker compose logs -f memtrix

# View web control panel logs
docker compose logs -f web

# Restart the agent only
docker compose restart memtrix

# Stop everything
docker compose down

# Rebuild after code changes
docker compose build && docker compose up -d
tip
You usually don't need to restart by hand - the Web Control Panel applies config changes with a safe in-place restart and streams the progress to your browser.
Reference

Performance & Startup

Memtrix is designed to connect and start responding within seconds, then do the heavy lifting - embedding, indexing, reasoning - in the background, off the request path.

Non-blocking startup

Rather than waiting 40-90 seconds for the embedding model and indexes before accepting messages, the agent connects to Matrix and starts responding right away. The expensive work is deferred:

  • The on-device embedding model is loaded lazily on first use, on a background thread - and warmed at boot off the request path so the first message doesn't pay the one-time load cost.
  • Initial conversation and documentation indexing runs on the periodic-sync thread, not the startup path.
  • The embedding model is a thread-safe lazy singleton shared across the conversation, docs, and reasoning-memory indexes - it loads once even with concurrent callers.

Embedding & CPU

The local embedding model (nomic-embed-text-v1.5) runs in-process. To keep the agent responsive while it indexes:

  • Indexing upserts in bounded batches that release the GIL between passes, so the Matrix event loop and request handler aren't starved.
  • Embedding is capped to leave one CPU core free for the agent. Override the thread count with the MEMTRIX_EMBED_THREADS environment variable.
  • Embeddings are truncated to 256 dimensions (Matryoshka) for efficiency without meaningful quality loss.

Incremental, restart-safe indexing

Conversation indexing is incremental and survives restarts via a per-chunk content-hash cache (.chunk-hashes.json stored alongside the index):

  • Growing conversations only embed their newest chunks.
  • Deleted sessions are pruned from the index.
  • Warm starts skip re-embedding unchanged history.
  • A chunk is re-embedded whenever its hash changed or it is missing from the collection, so a wiped or partially rebuilt index heals itself.

The documentation index is content-hashed too - it only rebuilds when the docs actually change. Both run a background sync roughly every 300 seconds.

Background work

WorkerWhat it does
HeartbeatWrites agent liveness every ~10s so the Web Control Panel can tell if the agent is up
DeriverReasoning-memory extraction and daily consolidation (see Reasoning Memory)
Conversation syncEmbeds new conversation chunks on a periodic interval
Docs syncKeeps the documentation index current

First start vs. warm start

  • First start downloads the embedding model (~100 MB) into data/models/ and, for voice, the whisper model into data/models/ on first use. The agent stays responsive throughout.
  • Warm start reuses the cached models and the hash caches, so there is no re-download and only new/changed content is re-embedded.
tip
On a busy machine, lowering MEMTRIX_EMBED_THREADS keeps the agent snappier during large reindex passes at the cost of slower indexing. On a dedicated box you can raise it to index faster.
Reference

Troubleshooting

Common issues and how to fix them.

Conduit won't start

Check if port 6167 is already in use:

lsof -i :6167

Verify the Conduit container is running:

docker compose ps conduit

Bot doesn't respond

  • Check logs: docker compose logs -f memtrix
  • Verify onboarding completed: the log should show "Starting Memtrix..." at boot
  • Make sure you invited the bot to the room (check the exact username from onboarding)
  • Check that the LLM provider is reachable (Ollama running? OpenRouter key valid?)

Can't connect to Ollama

If Ollama runs on your host machine, the container needs to reach it. Use:

http://host.docker.internal:11434

On Linux, you may need to add --add-host=host.docker.internal:host-gateway to the container or use your machine's LAN IP.

Embedding model download fails

Memtrix downloads nomic-embed-text-v1.5 on first launch. If it fails:

  • Check internet connectivity from the container
  • Ensure data/cache/ is writable (should be owned by uid 1000)
  • Try restarting: docker compose restart memtrix

Permission errors

All mounted directories should be owned by uid 1000:

sudo chown -R 1000:1000 workspace/ agents/ data/

Session or memory issues

  • /clear (or /new) - resets the current session
  • Sessions are stored in data/ as JSON files organized by date
  • Memory files are in workspace/memory/
  • Reasoning-memory conclusions live in the chroma service - browse, edit, or export them from the Web Control Panel

Web Control Panel won't load

  • Confirm the service is up: docker compose ps web
  • Check logs: docker compose logs -f web
  • Make sure nothing else uses port 8800 (lsof -i :8800), or change MEMTRIX_WEB_PORT
  • If the API returns 401, set or re-enter the MEMTRIX_WEB_TOKEN the panel asks for

Memory not persisting / Chroma errors

  • Check the vector store is running: docker compose ps chroma
  • Verify both memtrix and web point at CHROMA_URL=http://chroma:8000
  • Inspect logs: docker compose logs -f chroma
  • If memory is paused, the deriver won't write new conclusions - check the panel's deriver toggle

Full reset

warn
This deletes all data - config, sessions, memory, persona files, sub-agents. Only do this if you want a fresh start.
docker compose down
rm -rf data/ workspace/ agents/ .env
./setup.sh
./onboard.sh
docker compose up -d
Reference

Changelog

Every release, newest first. This page is rendered live from CHANGELOG.md in the repository - the single source of truth.

Loading changelog…