{"id":"ca5d6675-6160-4423-8c5c-c02b9b97754a","slug":"fleetmem","kind":"mcp","name":"fleetmem","summary":"Self-hosted, governed long-term memory for a fleet of AI agents — MCP-native, with hybrid retrieval, a typed knowledge graph, and per-agent mTLS access control. Runs on your own hardware; ships empty","owner":"aurelius86","repoUrl":"https://github.com/aurelius86/fleetmem","license":"AGPL-3.0","licenseTier":"strong-copyleft","trust":"community","stars":0,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-07-26T13:10:14.000Z","addedAt":"2026-10-21T06:29:58.646Z","logoUrl":"https://avatars.githubusercontent.com/u/129121053?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["memory-knowledge","search","databases"],"tags":["agent-memory","agents","ai","ai-agents","knowledge-graph","llm","memory","pgvector"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-23T12:29:06.585Z","sample":false,"url":"https://conduits.dev/tools/fleetmem","language":"Python","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# fleetmem\n\n[![CI](https://github.com/aurelius86/fleetmem/actions/workflows/ci.yml/badge.svg)](https://github.com/aurelius86/fleetmem/actions/workflows/ci.yml)\n[![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](LICENSE)\n[![Version](https://img.shields.io/badge/version-0.1.6-blue.svg)](CHANGELOG.md)\n[![MCP](https://img.shields.io/badge/protocol-MCP-blue.svg)](https://modelcontextprotocol.io)\n\n**Self-hosted, governed long-term memory for your AI agents.**\n\nfleetmem is an open, self-hosted memory and knowledge-graph server for *teams* of AI agents. Agents\nstore facts, decisions, and lessons; fleetmem indexes them by meaning (hybrid vector + keyword search),\nlinks them into a **typed knowledge graph** (`supersedes` / `conflicts_with` / `depends_on` / …), and\n**governs who sees what** (per-agent mTLS + roles; a `personal → shared → trusted` lifecycle). Connect\nany LLM agent over **MCP** and it gains durable, shared, access-controlled memory.\n\nIt runs entirely on your own hardware. It ships with **no memory and no credentials** — an empty\nbrain that mints its own security material on first run.\n\n---\n\n## Contents\n\n- [Why](#why) · [What you get](#what-you-get) · [Core concepts](#core-concepts)\n- [What it looks like](#what-it-looks-like) · [Features](#features) · [Architecture](#architecture)\n- [Security model](#security-model) · [Install](#install) · [Configuration](#configuration)\n- [Interfaces](#interfaces) · [Where fleetmem fits](#where-fleetmem-fits) · [FAQ](#faq)\n- [Documentation map](#documentation-map) · [Status](#status) · [License](#license)\n\n---\n\n## Why\n\nLLM agents forget everything between sessions. Bolting on a vector store gives you search but not\n*governance* — no identity, no access control, and no sense of which memory supersedes which. That\nbreaks down the moment you have more than one agent:\n\n- **Whose memory is this?** A flat store has no author, so you can't tell a verified fact from\n  something a small local model hallucinated last Tuesday.\n- **Who's allowed to read it?** One shared pool means your ops bot can read anything your personal\n  assistant wrote.\n- **Which fact is current?** Notes contradict each other over time, and plain similarity search\n  happily returns the stale one.\n- **How does it stay clean?** Without dedup, linking, and retention, the store rots.\n\nfleetmem is the **governed layer**: every write is attributed to an authenticated agent, every read is\nfiltered by role and sensitivity, and a typed graph keeps knowledge coherent as it grows.\n\n## What you get\n\n- A **memory server** your agents talk to over MCP — 60+ tools, no client library needed\n  (a manager sees the full set; workers see a governed subset).\n- **Hybrid retrieval** that returns *how notes connect*, not just what matched — with optional\n  on-demand LLM re-ranking and span-level body compression.\n- **Governance**: per-agent identity, reader groups, a sensitivity ceiling, and a review queue.\n- **Autolearn**: memories extracted from your sessions automatically, then gated.\n- **More than notes**: tasks, projects, ideas, skills, sessions/transcripts, and agent-to-agent messages.\n- **A web dashboard** for the graph, approvals, config, skills, and per-agent settings.\n- **Maintenance that runs itself**: edge classification, verification, retention, re-embedding.\n\n## Core concepts\n\n### Memories\nA memory is a named note with a body, a `description`, an author, a type (`reference`, `feedback`,\n`project`, `decision`, …), tags, and a sensitivity level. Agents write them with `brain_remember`\n(personal) or `brain_propose` (straight to the shared review queue).\n\n### The lifecycle: `personal → ready_to_share → trusted`\nCapture is **never blocked** — anything an agent learns lands immediately as its **own personal,\nuntrusted** note that only it can recall. Trust is earned *later*:\n\n| State | Who can read it | Meaning |\n|---|---|---|\n| `personal` | the author only | captured, not yet validated |\n| `ready_to_share` | author + managers | proposed for sharing, in the review queue |\n| `trusted` | per reader groups + sensitivity ceiling | validated and shared |\n\nRecall tells an agent when a note is untrusted and hands back the **source session**, so the agent can\ncheck it against the transcript and then `brain_validate_memory` it — or delete it. Sharing across\nagents always needs a manager. The effect: **no agent silently inherits another agent's mistake.**\n\n### The typed knowledge graph\nNotes link to each other with typed edges — `relates_to`, `supersedes`, `conflicts_with`,\n`depends_on`, `runs_on`, `accessed_via`, `uses`. Edges come from three places:\n\n1. **Hand-typed** at write time — put `[[target|rel_type]]` in a note body (`[[target]]` = `relates_to`).\n2. **Co-use links** — new notes auto-link to their nearest semantic neighbours.\n3. **A classifier job** that types untyped edges with a local LLM, precision-first: a relation is only\n   promoted if it survives all gates, otherwise it stays `relates_to`. Uncertain types go to a review\n   queue rather than being guessed into the graph.\n\nThe vocabulary lives in `graph.yaml`, not in code — add your domain's types (e.g.\n`invoiced_to` / `archived_in`) with a config edit and the classifier picks them up.\n\n### Identity and access\nEvery agent is a row in the `agent` table with a **role** (`manager` / `worker` / `readonly`), a\n**lane**, a **sensitivity ceiling**, and **reader groups**. Two factors are required on every call:\nan **mTLS client certificate** (CN = the agent's name) *and* a **bearer token** (only its hash is\nstored). Row-Level Security in Postgres is the backstop under the application's own checks.\n\n### It isn't only memories\nThe same governed store also holds **tasks**, **projects** (each with a living plan document),\n**ideas**, **sessions** (ingested transcripts, searchable), **messages** between agents, an\n**infra model**, and an **audit log**.\n\n## What it looks like\n\nOnce the MCP is wired, your agent just calls tools:\n\n```\nbrain_remember(\n  name=\"pg_upgrade_gotcha\",\n  body=\"Upgrading Postgres in place needs the pgvector extension rebuilt, \"\n       \"or recall silently returns zero rows. See [[deploy_runbook|depends_on]].\",\n  mtype=\"reference\", tags=[\"postgres\",\"gotcha\"]\n)\n→ saved: personal, usable immediately by you, invisible to everyone else.\n\nbrain_recall(\"why did recall return nothing after the db upgrade\", k=5)\n→ your note, plus the notes it links to, plus anything sharing a rare entity\n  (\"pgvector\") with the query — each tagged trusted / untrusted.\n\nbrain_share(id)        → sends it to the review queue\nbrain_propose(...)     → skips personal, straight to review\n```\n\nNothing here is bespoke: a manager approves it, it becomes `trusted`, and *every other agent you run*\ncan now recall it — subject to reader groups and its sensitivity ceiling.\n\n## Features\n\n### Hybrid retrieval\nDense vectors (via a local embedding model) and keyword search (Postgres full-text) are fused with\n**Reciprocal Rank Fusion**, then expanded two ways:\n\n- **Graph expansion** — pull in what the hits link to, so you see relationships, not isolated matches.\n- **Entity expansion** — pull in notes sharing a *rare* entity with the query (task ids, hostnames,\n  filenames), weighted by rarity; hub entities are skipped so common terms don't spray noise.\n\nA trigram index catches typos and out-of-vocabulary terms that exact-token search misses.\n\nTwo optional refinements, both **off by default** (base hybrid recall is already at ceiling for a\nwell-curated brain):\n\n- **On-demand re-ranking** — pass `rank=true` to `brain_recall` and the content hits are reordered by\n  the local LLM before returning; it **fail-safes to the original order** on any timeout, so recall\n  never stalls.\n- **Span-level compression** (`RECALL_SPAN_COMPRESS`) — trims each recalled body to its most\n  query-relevant spans (cheap lexical scoring, no extra embed calls; the first span is always kept),\n  ~85% smaller bodies on long notes for thinner injected context. The full body is always fetchable\n  via `brain_get`.\n\n**Recall stays honest under strain:** a transient embedder blip is ridden out with a bounded\nbackoff-retry, and if the dense arm does stay down the degradation is *surfaced* (a `recall_degraded`\naudit event + a `degraded` field on the response) instead of silently returning weaker results.\n\n### Skills\nReusable how-to notes (\"skills\") are a first-class corpus: agents fetch them with `brain_skill_recall`\n/ `brain_skill_get`, and the dashboard's **Skills** tab lists, filters, and opens them.\n\n### Autolearn\nAt session end, a transcript can be handed to fleetmem, which extracts durable candidate memories,\nscrubs secrets **client-side first**, runs a worthiness judge, checks for duplicates and conflicts,\nconsults a **lessons** table (so it never re-learns something you already rejected), and lands what\nsurvives as personal/untrusted notes. Every step is configurable, and the whole thing can be turned off.\n\n### Sessions and transcripts\nTranscripts are ingested (redacted), indexed for full-text and vector search, and become the\n**source of truth** an agent validates its own memories against. `rebuild_session.py` can re-derive\nmemories and edges from a transcript after the fact.\n\n### Governance and review\nA `/needs-you` queue surfaces **only** what genuinely needs a human — share requests and escalations —\nrather than every note. Managers approve, deny, or escalate. A manager may not graduate its *own*\nnote (author ≠ validator). For retiring a stale *trusted* note there's a sanctioned, audited\nmanager delete (`brain_curate_delete`, reason required, reversible soft-delete) — no raw SQL needed.\n\n### Dashboard\nA small web UI ships under `dashboard/`: the knowledge graph, the approval queues, a memory explorer,\na System tab (jobs, session rebuild), a Config tab, and an Agents tab with a per-agent\n**session-start injection preview** — exactly what a given agent will be told at boot.\n\n### Maintenance that runs itself\nEight systemd timers ship: edge classification, infra-edge derivation, queue drain, golden-set recall\neval, memory verification, re-embedding, retention pruning, and the validation sweep. All are\nschedulable and runnable on demand from the dashboard.\n\n### Sensitivity routing\nExtraction backends are pluggable and **routed by sensitivity**: content marked sensitive or secret\nnever leaves your box for a cloud model, whatever the cloud setting says. `test_pick_backend.py`\nships as a standalone proof you can run yourself.\n\n## Architecture\n\n```mermaid\nflowchart LR\n    subgraph agents[\"your agents (any MCP runtime)\"]\n        A1[\"Claude Code\"]\n        A2[\"OpenClaw / Hermes\"]\n        A3[\"custom agent\"]\n    end\n    A1 --> M[\"MCP server (brain_* tools)\"]\n    A2 --> M\n    A3 --> M\n    M -- \"mTLS cert + bearer token\" --> N[\"nginx (TLS + client-cert verify)\"]\n    N --> API[\"Flask API — auth, roles, RLS, governance\"]\n    API --> PG[(\"PostgreSQL + pgvector — memories, typed graph, tasks/projects/ideas, sessions, audit log\")]\n    API -- \"embeddings + extraction\" --> LLM[\"local LLM (Ollama)\"]\n    DASH[\"dashboard (web UI)\"] --> API\n    TIMERS[\"systemd timers — edge classify, memory verify, retention prune, ...\"] --> PG\n```\n\nEvery arrow into the API is authenticated (client cert **and** token); every read is filtered by\nrow-level security (role, reader groups, sensitivity ceiling). The local LLM handles embeddings and\nsession-end extraction.\n\n## Security model\n\n- **Two factors, always.** mTLS client cert + bearer token on every call except open enrollment.\n- **The box mints its own CA.** `fleetmem-init-pki.sh` (plain openssl) creates a local CA and server\n  cert at install. No external or cloud PKI, and **no key material ships** in this repo.\n- **Signing stays off the network service.** `sign-agent-cert.sh` signs agent CSRs out-of-band, so\n  compromising the API can never mint certs.\n- **Defence in depth.** Application checks are backed by Postgres Row-Level Security; the app role's\n  privileges are scoped (no `DELETE` on tables the app never hard-deletes).\n- **Tokens are hashed.** Plaintext is shown once at enrollment and never stored.\n- **Enrollment is n-of-m.** You choose how many managers must co-approve a new agent.\n\nReporting a vulnerability: see [`SECURITY.md`](SECURITY.md).\n\n## Install\n\n**Requirements:** PostgreSQL 15+ with `pgvector`, Python 3.11+, `openssl`, nginx, and a local LLM\nendpoint (Ollama or compatible) for embeddings and extraction. Install is systemd + venv; Docker\npackaging is planned but does **not** ship yet.\n\nThe fastest path is to **point your LLM agent at [`AGENTS.md`](AGENTS.md)** — it's a runbook written\nfor an agent to execute: bring up the server, create your first (genesis) manager, wire the MCP.\n\nManually:\n\n```bash\ncp brain.env.example brain.env     # set your DB + Ollama endpoints\nsudo ./install.sh                  # Postgres + pgvector, migrations, PKI, systemd + nginx\n./fleetmem-bootstrap-manager.py      # your first manager: you pick the name; prints cert + token ONCE\n./smoke_test.sh                    # verifies the front door, mTLS, and a real recall\n```\n\nThen follow [`INSTALL.md`](INSTALL.md) to connect an agent, and [`ENROLL.md`](ENROLL.md) to add more.\nUpgrades are one command (`./update.sh`) and preserve your data, config, and PKI — see\n[`UPGRADING.md`](UPGRADING.md).\n\n## Configuration\n\nTwo layers:\n\n- **`brain.env`** — connection-level settings, read at boot: `PGDATABASE`, `OLLAMA_EMBED_URL`,\n  `EMBED_MODEL`, `OLLAMA_GEN_URL`, `EXTRACT_MODEL`, `RERANK_MODEL`.\n- **The live config table** — ~21 tunables you can read and change at runtime via `GET`/`PATCH\n  /config` or the dashboard's Config tab, with no restart. Highlights:\n\n| Knob | What it controls |\n|---|---|\n| `RECALL_K`, `RECALL_POOL`, `RELATED_CAP` | recall breadth and how much graph context comes back |\n| `ENTITY_EXPAND_WEIGHT`, `ENTITY_EXPAND_HUBCAP` | entity-expansion strength; hub-entity cutoff |\n| `AUTOLEARN_LLM_JUDGE`, `AUTOLEARN_DEDUP_COSINE`, `AUTOLEARN_LINK_CAP` | autolearn gating and linking |\n| `AUTOLEARN_LAND_SENSITIVE`, `AUTOLEARN_LAND_QUARANTINED` | where autolearn output lands |\n| `RECALL_SPAN_COMPRESS`, `RECALL_SPAN_COUNT`, `RECALL_SPAN_MIN_CHARS` | span-level body compression on recall (off by default) |\n| `PROVISIONAL_TTL_DAYS`, `VALIDATE_SWEEP_DELETE` | lifecycle and the retention backstop |\n| `ENROLL_APPROVALS` | how many managers must approve a new agent |\n| `ENTITY_IP_PREFIXES` | optional site subnets to treat as entities (unset by default) |\n\nThe relation vocabulary lives in [`graph.yaml`](graph.yaml); the embedding model is pinned in\n`model-pin.json` so a silent model swap is detectable.\n\n## Interfaces\n\n- **MCP — 60+ tools** (a manager sees the full set; workers see a governed subset). Recall and search\n  (`brain_recall`, `brain_deep_search`, `brain_search_transcripts`, `brain_get`, `brain_schema`,\n  `brain_docs_recall`, `brain_skill_recall`, `brain_skill_get`); writing and governance\n  (`brain_remember`, `brain_propose`, `brain_share`, `brain_validate_memory`, `brain_curate_delete`,\n  `brain_my_provisional`, `brain_get_session_turns`); work tracking (`brain_tasks`,\n  `brain_add_task`, `brain_update_task`, `brain_projects`, `brain_add_project`, `brain_ideas`,\n  `brain_add_idea`); attachments, tags, infra, and agent-to-agent messaging (`brain_send`,\n  `brain_inbox`). Full reference with worked examples: [`USAGE.md`](USAGE.md).\n- **HTTP — ~89 endpoints** for everything the MCP exposes plus operator surfaces (`/healthz`,\n  `/whoami`, `/bootstrap`, `/config`, `/jobs`, `/agents`, `/graph`, `/audit`, `/needs-you`,\n  `/session/ingest`, `/session/search`, …). `/healthz` reports the running version.\n- **Dashboard** — the web UI under `dashboard/`.\n\n## Where fleetmem fits\n\nfleetmem is **not** an agent framework — it's the shared **memory layer** they plug into. Runtimes like\n**OpenClaw** and **Hermes Agent** *run* agents (chat channels, tool execution, one agent's own\nmemory); fleetmem is a **standalone, shared, governed memory server** that any number of agents — on any\nframework — point at over MCP. Keep your runtime; add fleetmem so all your agents read and write **one\naccess-controlled long-term brain**. In short: *they run the agents; fleetmem is the shared brain you\ngive them.*\n\nCompared with a plain vector store (pgvector, Qdrant, Chroma) or a hosted memory API: those give you\nsimilarity search. fleetmem adds identity, access control, a typed graph, a validation lifecycle, and\nthe maintenance jobs that keep a store from rotting — and it runs on your hardware.\n\n## FAQ\n\n**Does any data leave my machine?**\nNo. Embeddings and extraction run against your local LLM endpoint. A cloud extraction backend is\n*optional* and sensitivity-routed — sensitive/secret content never routes to it.\n\n**Do I need Claude / a specific model?**\nNo. Any MCP-capable agent works. The local LLM is Ollama-or-compatible; the embedding model is your\nchoice (pinned for reproducibility).\n\n**Does it ship with your data?**\nNo. The database starts empty and the box mints its own CA and tokens on first run.\n\n**Can I run one agent?**\nYes — governance stays out of your way (a single manager self-approves). The multi-agent machinery\nmatters when you add a second.\n\n**What if the graph types are wrong for my domain?**\nEdit `graph.yaml`. Types are config, not code, and there's a discovery script that proposes an\nontology from your own notes.\n\n**Is it production-ready?**\nIt's `v0.1.x` and runs a real daily workload, but the API may still change between minor versions.\nRead [`CHANGELOG.md`](CHANGELOG.md) before upgrading.\n\n## Documentation map\n\n| Doc | What's in it |\n|---|---|\n| [`AGENTS.md`](AGENTS.md) | agent-executable setup runbook (the quickest install) |\n| [`INSTALL.md`](INSTALL.md) | manual install + connecting an agent |\n| [`ENROLL.md`](ENROLL.md) | adding more agents; the n-of-m approval flow |\n| [`USAGE.md`](USAGE.md) | day-to-day manual: every MCP tool, the memory model, a worked walkthrough |\n| [`SECURITY.md`](SECURITY.md) | security model and vulnerability reporting |\n| [`UPGRADING.md`](UPGRADING.md) | what's preserved, one-command update, rollback |\n| [`CHANGELOG.md`](CHANGELOG.md) | release notes |\n| [`SCHEMA-SCAFFOLD-NOTES.md`](SCHEMA-SCAFFOLD-NOTES.md) | schema scaffolding and dead-column guide |\n| [`CONTRIBUTING.md`](CONTRIBUTING.md) | how to contribute (inbound = AGPL) |\n\n## Status\n\n`v0.1.6`. The engine, governance, graph, autolearn, and dashboard are in daily use. Known gaps:\nDocker packaging isn't shipped (systemd + venv only), and there's no built-in HA/failover — a single\nPostgres instance, so back it up.\n\n## License\n\n**GNU AGPL-3.0** — see [`LICENSE`](LICENSE). You may run and modify fleetmem freely (including\nprivately); if you distribute it or run a modified version as a network service, you must share your\nsource under the AGPL. Founded and maintained by **Aurelius** (see [`NOTICE`](NOTICE) /\n[`AUTHORS`](AUTHORS)); contributions welcome under [`CONTRIBUTING.md`](CONTRIBUTING.md).\n</content>\n</invoke>\n"}