Engram: persistent memory for agents on Linux

Mascote do LinuxPro encaixando um cartucho de memória no SQLite, com o elefante do Engram na bancada

The terminal agent is great until the session ends. In the next one, it rediscovers the same bug, rereads the same README, and asks again if the API uses JWT. The Engram is a local brain for this: a binary at Go, a SQLite with FTS5 at ~/.engram/engram.db, and the same database for Claude Code, Codex, Cursor, Grok, and any other MCP client.

It's not a transcript dump. The agent decides what's worth remembering — decision, bug, convention — and writes with mem_save. In the next session, mem_search and mem_context they return the right chunk. The map of alternatives (mnemo, Leteo, Subcog…) is at Claude-Mem alternatives in Go, Rust, and C; the map of terminal agents, at AI assistants on the Linux terminal. This guide walks through setting up Engram on Linux: installation, connecting to agents, configuring the project name, and enabling the daemon at boot.

What it is — and what it isn't

Engram (/ˈen.ɡræm/, the physical trace of a memory in the brain) is a Go binary with embedded SQLite via modernc.org/sqlite — pure Go, no CGO, no Node, no Python, no Docker for daily use. MIT license. The stable line in July of 2026 is the v1.20.0.

Four ports for the same database:

Port Command What for
MCP stdio engram mcp --tools=agent The agent talks with memory (Claude, Grok, Codex…)
CLI engram search, save, context You manually query and save
TUI engram tui Browse sessions and observations in the terminal
Local HTTP engram serve (port 7437) JSON API. No is browser page

The HTML dashboard (/dashboard) is the Engram Cloud — another process, Postgres, optional. Without Cloud, the local viewer is the TUI.

The philosophy, explicit in the README: the agent already has the LLM and the context. Instead of capturing every tool call and compressing afterward (the claude-mem model), Engram asks the agent to record only what lasts. Result: clean FTS5 search, zero extra API calls, one file you control.

Install on Ubuntu/Linux

There is no package apt neither .deb in the releases. What the production documentation asks for is the binary. One file, zero runtime: no Node, no Python, no Docker. The stable branch in July 2026 is the v1.20.0.

The releases provide ready-to-use Linux binaries for amd64 and arm64. The official executable is statically linked, with no runtime dependencies; just pick the file for your machine's architecture. To compile from source, the requirement is Go 1.24+.

Ubuntu amd64 and arm64

sudo apt-get update
sudo apt-get install -y curl ca-certificates

VER=1.20.0
case "$(dpkg --print-architecture)" in
  amd64) FILE=engram_${VER}_linux_amd64.tar.gz ;;
  arm64) FILE=engram_${VER}_linux_arm64.tar.gz ;;
  *) echo "sem binário pronto para $(dpkg --print-architecture)"; exit 1 ;;
esac

cd /tmp
curl -fsSL -O "https://github.com/Gentleman-Programming/engram/releases/download/v${VER}/${FILE}"
curl -fsSL -O "https://github.com/Gentleman-Programming/engram/releases/download/v${VER}/checksums.txt"
sha256sum -c checksums.txt --ignore-missing

mkdir -p ~/.local/bin
tar -xzf "$FILE" engram
install -m 755 engram ~/.local/bin/engram

# o ~/.profile do Ubuntu só acrescenta ~/.local/bin no próximo login,
# e só se o diretório já existir
export PATH="$HOME/.local/bin:$PATH"
hash -r
engram version

The expected output is engram 1.20.0. The tarball contains the executable at the root, plus README, LICENSE, and CHANGELOG. Default database: ~/.engram/engram.db.

To make the PATH permanent, open a new terminal after installation. The ~/.profile Ubuntu's default already has the block if [ -d "$HOME/.local/bin" ] — so the mkdir comes before the install.

go install

If you already have Go 1.24+ (the one from the documentation). golang-go from Ubuntu 24.04 is 1.22 and the one from 22.04 is 1.18 — you can't go install that way; use the release binary or install Go from the website:

go install github.com/Gentleman-Programming/engram/cmd/engram@latest
# cai em $(go env GOPATH)/bin — em geral ~/go/bin/

go install stamps the version as dev. If you want the git-describe in engram version, clone and pass -ldflags — a go install and a go build in the same PATH leave two binaries and the “dev” version wins. There's a v2 line (github.com/Gentleman-Programming/engram/v2/cmd/engram) for release candidates; production continues on v1.

Homebrew on Linux

brew install gentleman-programming/tap/engram

The tap covers the v1 stable line.20.0. Upgrade: brew update && brew upgrade engram. Swapping the binary kills a engram serve loose — that's why the systemd service, below.

First contact

engram version
engram stats
engram projects list
engram tui

Default database: ~/.engram/engram.db. Override: ENGRAM_DATA_DIR. In the TUI, j/k navigates, Enter opens, / searches, Esc returns. Catppuccin Mocha theme.

The local API, if you start the daemon:

engram serve          # 127.0.0.1:7437
curl -s http://127.0.0.1:7437/health
# {"service":"engram","status":"ok","version":"0.1.0"}

/ and /dashboard on this port they return 404. It's JSON, not a site. GET /project/current use the cwd of the serve process — on systemd with WorkingDirectory=%h, this becomes the basename of home, not the repo you're editing.

Connect to agents

engram setup <agente> writes MCP in the format of each client. Doesn't configure Cloud. Restart the agent afterwards.

Agent Command
Claude Code claude plugin marketplace add Gentleman-Programming/engram and claude plugin install engram — or engram setup claude-code
Codex engram setup codex
OpenCode engram setup opencode
Cursor engram setup cursor
Gemini CLI engram setup gemini-cli
VS Code Copilot engram setup vscode-copilot
Windsurf, Qwen, Kiro, Kilo, Pi, Antigravity engram setup <nome> — list in engram --help

MCP Profiles: agent (the set that the agent uses on a daily basis), admin, or all. The default of engram mcp without flag is all. For the agent, use --tools=agent.

Grok Build (manual)

There is no engram setup grok. In ~/.grok/config.toml:

[mcp_servers.engram]
command = "/home/SEU_USER/.local/bin/engram"
args = ["mcp", "--tools=agent"]
enabled = true

Confira com grok mcp doctor engram — handshake ok e as tools do perfil agent. Recarregue a sessão do Grok: o MCP só entra na que sobe depois da config. O banco é o mesmo do Claude. O nome do projeto, não: cada processo detecta pelo próprio cwd.

Any MCP client

{
  "mcpServers": {
    "engram": {
      "command": "engram",
      "args": ["mcp", "--tools=agent"]
    }
  }
}

O cliente dispara um processo stdio por sessão. Isso não substitui o engram serve do systemd — são coisas diferentes. MCP lê e grava o SQLite direto; o serve é a API HTTP (e o autosync Cloud, se você ligar).

How Engram chooses the project

Ele não indexa o path. Resolve um nome e filtra o SQLite por esse nome. Precedência:

  1. argumento explícito (mem_context with project, engram context teste);
  2. override de processo (engram mcp --project … or ENGRAM_PROJECT);
  3. detecção pelo cwd.

A detecção do diretório, em ordem:

# source O que usa
0 config .engram/config.json with project_name
1 git_remote repo com origin — nome do remote, gravado num binding privado do clone
2 git_root git sem origin — basename da raiz
3 git_child cwd tem exatamente um filho git — promove esse
4 ambiguous vários filhos git — não escolhe sozinho
5 dir_basename último recurso: filepath.Base(cwd)

Pasta sem git vira o basename. Claude gravando em /home/você/teste e Grok em …/site-linuxpro são dois projetos. A memória não aparece “do outro lado” até você buscar com o nome certo ou all_projects=true.

Lock estável na raiz do repo:

{
  "project_name": "site-linuxpro"
}

O Engram usa o config mais próximo abaixo da raiz git — dá para ter backend/.engram/config.json and frontend/.engram/config.json no monorepo. Fora de git, só vale o config do diretório atual: ~/.engram/config.json não vaza para os filhos. Primeira chamada útil de qualquer sessão: mem_current_project — devolve project, project_source e, se for o caso, available_projects.

Se a escrita voltar ambiguous_project, o agente não pode chutar. Você escolhe um nome da lista e a tool retenta com project_choice_reason=user_selected_after_ambiguous_project.

Start the server on boot

Esta seção é opcional. Claude Code, Codex e os demais clientes MCP via stdio usam engram mcp e não precisam de engram serve. Suba o serviço se precisar da API HTTP local, do autosync do Cloud ou de integrações HTTP, como OpenCode e Pi.

Quando ele for necessário, um supervisor evita que engram serve morra no reboot ou durante a troca do binário.

Unit de usuário, no template oficial:

# ~/.config/systemd/user/engram.service
[Unit]
Description=Engram Memory Server
After=network.target

[Service]
WorkingDirectory=%h
ExecStart=%h/.local/bin/engram serve
Restart=always
RestartSec=3
Environment=ENGRAM_DATA_DIR=%h/.engram
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=default.target
mkdir -p ~/.engram ~/.config/systemd/user
systemctl --user daemon-reload
systemctl --user enable --now engram.service
curl -s http://127.0.0.1:7437/health
journalctl --user -u engram -f

enable amarra no login. Para subir no boot without sessão gráfica, o usuário precisa de linger:

loginctl enable-linger "$USER"
loginctl show-user "$USER" -p Linger

Se a 7437 já estiver ocupada por um serve órfão, a unit falha no bind. Mate o processo antigo (ss -tlnp | grep 7437) e systemctl --user restart engram.

The memory contract

O README trata o Engram como memória curada, não como dump. O ciclo:

  1. Orientarmem_current_project, depois mem_context / mem_search.
  2. Buscar antes de repetir — decisão, bug, convenção que já podem estar no banco.
  3. Revelar por camadasmem_search devolve preview (~300 caracteres); mem_get_observation traz o texto inteiro; mem_timeline mostra o antes/depois na sessão.
  4. Gravar o que duramem_save com título curto, tipo (decision, bugfix, config, architecture…) e corpo What / Why / Where / Learned. Não grave saída crua de ferramenta.
  5. Assunto que evoluitopic_key estável (architecture/auth-model). Mesmo projeto + scope + topic vira upsert, não uma linha nova.
  6. Fechar a sessãomem_session_summary com Goal, Discoveries, Accomplished, Next Steps, Files.

Scope: project (default), personal or global. Um personal no mesmo topic_key não sobrescreve o de projeto.

O que não vai para o SQLite: senha, token, dump de journalctl. O git continua sendo o registro — a história está em A história do Git. Tags <private> são removidas em duas camadas no Engram; mesmo assim, não grave o que não pode vazar.

Cloud and dashboard in the browser

O Cloud é replicação opcional + UI. SQLite local continua autoritativo. Caminho smoke com Docker, no clone do repositório:

docker compose -f docker-compose.cloud.yml up -d
engram cloud config --server http://127.0.0.1:18080
engram cloud enroll meu-projeto
engram sync --cloud --project meu-projeto

Dashboard local: http://127.0.0.1:18080/dashboard. No compose smoke, /dashboard/login redireciona sem pedir token. Rotas úteis: /dashboard/browser, /dashboard/projects, /dashboard/activity.

Para o dia a dia num laptop, Cloud é extra. TUI + MCP bastam.

Tips to avoid pain

  • Um cérebro, nomes certos. Claude, Grok e Codex só compartilham memória se o project for o mesmo. Coloque .engram/config.json no repo no primeiro dia.
  • Não instale três servidores de memória no mesmo agente. Eles competem pelo contexto. Um de sessão (Engram) + o grafo do código (codebase-memory-mcp) já é o teto útil — o encaixe no fluxo está em Vibe coding on Linux.
  • MCP ≠ serve. O Grok/Claude sobe engram mcp via stdio. O systemd sobe engram serve na 7437. Os dois podem coexistir; um não substitui o outro.
  • Cwd do serve. Unit com WorkingDirectory=%h faz /project/current responder o basename da home. Para leitura de projeto, use o MCP do agente (cwd do repo) ou passe --project.
  • Busca FTS5 é AND por padrão. match_mode=any alarga. Preview não é o registro completo — abra o id.
  • Doctor. engram doctor (ou mem_doctor) checa lock do SQLite, mismatch de sessão e binding. Quatro checks ok é o estado saudável.
  • Linger. Sem loginctl enable-linger, a unit de usuário só vive enquanto houver sessão. Com linger, sobrevive ao reboot headless.
  • Upgrade do binário. Homebrew ou um install por cima mata o processo se não houver systemd com Restart=always.
  • Não misture autosync nativo e wrappers de cron. A documentação pede um modo só.
  • HTTP token. ENGRAM_HTTP_TOKEN protege delete/export/import na API local. Sem ele, essas rotas ficam abertas em 127.0.0.1 — ok no laptop, ruim se você publicar a 7437 na LAN.

Engram or claude-mem?

O Engram nasceu inspirado no claude-mem, com decisões opostas: binário único, qualquer agente MCP, FTS5 em vez de worker + ChromaDB, o agente comprime na hora em vez de um pipeline à parte. Se a sua vida é só Claude Code e você já está no ecossistema de hooks dele, o original continua válido. Se Claude, Codex e Grok precisam lembrar a mesma decisão, comece pelo Engram. Comparação lado a lado e as outras opções em Go/Rust/C: Alternativas ao Claude-Mem.

Closing

Instale o binário da release, trave o nome do projeto num .engram/config.json, ligue o MCP em cada agente e deixe o engram serve no systemd de usuário. O resto é disciplina: gravar o que dura, buscar antes de repetir, fechar a sessão com um resumo. O agente da manhã seguinte já sabe que a autenticação é JWT — e você não paga um segundo modelo só para lembrar disso.

To continue: assistentes de IA no terminal, Ollama se a inferência também for local, e a história do Go — a língua em que o Engram é um arquivo só.

Official links: github.com/Gentleman-Programming/engram · releases · engram.gentlemanprogramming.com · Installation · Agent Setup