Self-Hosting Honcho for Hermes Agent: Long-Term Memory Without Another SaaS
I connected Hermes Agent to a self-hosted Honcho instance so preferences, project context, and useful history survive across conversations.
My agent remembered the current chat perfectly and forgot everything useful the moment I opened a new one. I wanted memory that survived the tab, without handing another service my entire working history.
So I connected Hermes Agent to a self-hosted Honcho instance.
This isn't about giving an AI a giant transcript and hoping it finds the right paragraph. Honcho stores conversations, builds representations of users and agents, and retrieves relevant context later. Hermes then injects that context into a new conversation, or queries it through memory tools. What you get is closer to continuity than to storage.
It remembers that I build Rust products with Dioxus and Axum, that I prefer direct answers, and that a given project has its own deployment convention. Those facts stay useful long after the original conversation has disappeared into session history.
That's the promise, anyway. Setting it up also taught me that an enabled memory provider isn't necessarily a working one. Authentication breaks quietly, background processing dies without telling you, and a status command will happily print OK while the useful part does precisely nothing.
So here's the setup, the architecture, and the traps I'd avoid the second time.
Why ordinary chat history isn't enough
A conversation has short-term memory by default. The model sees the messages in its current context window and refers back to them. That works fine until the conversation gets compacted, or I start a new session, or I switch from the desktop app to another interface. It also breaks down when the useful fact lives in a conversation from three weeks ago, or when the history is too large to inject without burying the current task.
Saving every transcript solves storage. It doesn't solve recall.
An agent needs to answer two different questions:
- What happened in that old conversation?
- What from that conversation still matters now?
The first is search. The second takes judgment.
Hermes already has local memory files and searchable session history, and Honcho sits beside those rather than replacing them. It adds a longer-lived model of the user and the agent, plus semantic search and reasoning over past conversations.
I like that split. A small explicit memory file is still the right home for durable facts I want to control by hand. Session search is still what I reach for when I need the exact conversation where a decision got made. Honcho earns its place in the fuzzy middle: preferences, recurring patterns, shifting project context, and the facts that quietly became important without anyone bothering to promote them into a file.
What Honcho actually runs
The self-hosted stack isn't one magical memory container. The official Compose setup runs four services:
- api, which accepts peers, sessions, messages, and queries on port 8000
- deriver, the background worker that turns raw conversation into observations, summaries, and peer representations
- database, PostgreSQL with pgvector, holding durable state and embeddings
- redis, for caching and background coordination
The deriver matters more than it first appears. If the API is healthy but the deriver isn't processing, your messages still get stored while the memory layer never becomes useful. Congratulations, you've built a very elaborate chat archive.
Honcho also needs an LLM provider and won't start without one, since it uses models for extraction, summaries, dialectic queries, and memory consolidation. Worth being clear-eyed about what that means: self-hosting the database doesn't keep all your conversation content on your machine. If Honcho calls a hosted model, the relevant content still leaves your server for inference.
Which gives you three useful deployment categories.
Managed Honcho
The least work, since Plastic Labs runs both the service and the memory models. This is where I'd start if I only wanted to find out whether persistent memory improves my workflow.
Self-hosted storage, hosted inference
The database, vectors, and memory state live on infrastructure I control, while Honcho still calls an external model provider for extraction and reasoning. This is the practical middle ground for most developers, and it's what I run. You get control over the durable memory without standing up a local model server.
Fully local
Honcho and the inference models both run on your own machines. Strongest privacy boundary, largest operational bill, paid in electricity, hardware, and evenings lost to model-server configuration. Pick it because the data genuinely requires it.
Starting Honcho with Docker
The upstream repository ships the Compose setup:
git clone https://github.com/plastic-labs/honcho.git
cd honcho
cp .env.template .env
cp docker-compose.yml.example docker-compose.ymlYour .env needs credentials for whichever model provider Honcho will use. The current upstream default expects LLM_OPENAI_API_KEY with an OpenAI-compatible text and embedding model, and you can point the Deriver, Dialectic, Summary, and Dream features somewhere else through the *_MODEL_CONFIG__TRANSPORT, *_MODEL_CONFIG__MODEL, and base URL overrides. Check the current docs before copying model names out of an old tutorial, because this part moves quickly.
Then build and start it:
docker compose up -d --buildDon't skip --build. The Compose file builds Honcho from source, so the first run takes a while.
Once it's up, check the API and the background worker separately:
docker compose ps
curl http://localhost:8000/health
docker compose logs deriver --tail 20Don't stop at the health endpoint. The upstream docs say plainly that it "only confirms the process is running. It does not check database or LLM connectivity." It also tells you nothing about whether migrations finished, whether embeddings work, or whether Hermes can authenticate.
All Compose ports bind to 127.0.0.1 by default, which is the right call. If Honcho only ever runs on the same machine as Hermes, leave it there. For a remote agent machine, put it behind HTTPS and authentication instead of hanging port 8000 out on the internet.
My own instance doesn't run from that Compose file. It runs as a Coolify service behind HTTPS on a private domain, with the api and deriver containers on the ghcr.io/plastic-labs/honcho:latest image. That's where the next section comes from: the moment you put Honcho behind a domain you have to turn authentication on, and that's where things got interesting.
The authentication trap
This was the annoying part, and it has three separate layers.
Authentication is off by default
A fresh self-hosted Honcho ships with AUTH_USE_AUTH=false, so every request is accepted. Fine on localhost. Not fine the moment the API has a public hostname.
Turning it on takes two steps. First, generate the secret:
python scripts/generate_jwt_secret.py # produces AUTH_JWT_SECRETSet AUTH_USE_AUTH=true and that secret in .env, then mint a token the server will actually accept. Inside a running container:
docker exec <api-container> /app/.venv/bin/python \
scripts/generate_jwt.py --admin --print-onlyTo revoke everything you've issued, rotate AUTH_JWT_SECRET and restart.
Cloud keys aren't self-hosted keys
Honcho Cloud API keys and self-hosted tokens are different animals. A managed key starts with hch-v2-. A self-hosted server with auth enabled wants a JWT signed with your AUTH_JWT_SECRET. Paste a cloud key from app.honcho.dev into a self-hosted config and you get:
{"detail": "Invalid JWT"}It'll never validate, no matter how many times you re-copy it.
Two upstream bugs in the token script
Both of these cost me time, and neither one is documented.
The first: uv run doesn't work inside the container. The obvious invocation fails.
$ docker exec <api-container> uv run python scripts/generate_jwt.py --admin
error: honcho-ai references a workspace ... not a workspace memberCall the virtualenv interpreter directly instead, at /app/.venv/bin/python.
The second one is nastier: the --expires flag mints tokens the server then rejects. It writes the JWT exp claim as an ISO 8601 string where the spec wants a numeric timestamp, so the server refuses its own freshly issued token.
401 Unauthorized
{"detail": "Invalid JWT"}Note that the error text is identical to the wrong-key case, which is exactly what made it slow to diagnose. Mint without --expires, and rotate the secret when you want to revoke.
Verify the token server-side first
Before touching any client config, prove the token works against the API on its own:
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}' \
https://honcho.example.com/v3/workspaces/listA 200 with a JSON page object means the credential is good. Anything else is a server or token problem, and no amount of client configuration will fix it. That one command separates "my token is wrong" from "my integration is wrong," which are the two failures that look completely identical from inside a chat client.
Connecting Hermes Agent
Hermes has a first-party Honcho memory provider, and the supported path is the CLI wizard:
hermes memory setup honchoDrop the provider name if you'd rather have the picker. For a local instance, give it the server base URL:
http://localhost:8000For a remote deployment, use its HTTPS base URL.
The /v3 distinction
Hermes wants the server base URL. The Honcho Claude Code plugin wants the versioned API endpoint. Same server, two different strings:
Hermes: https://honcho.example.com
Claude Code: https://honcho.example.com/v3That bit me while connecting the second client. Similar-looking settings aren't interchangeable just because they eventually talk to the same API.
Then check what Hermes actually resolved:
hermes memory statusThere's no hermes honcho subcommand, in case you go looking for one. hermes memory takes setup, status, off, and reset, and status is where the provider, resolved host, workspace, peers, and connection result show up.
My starting shape:
workspace: hermes
user peer: hauke
AI peer: hermes
session strategy: per-directory
recall mode: hybridSession strategy also accepts per-repo, per-session, and global. I use per-directory because it keeps a stable session mapping for work done from the same folder, which matches how I actually work: one project, one directory, one accumulating thread of context.
hybrid gives Hermes both automatic context injection and the explicit Honcho tools. That's a sensible default for a personal agent, since you get useful standing context without needing the model to remember to search every time, and the tools are still there when a task needs deeper recall.
One warning about reading the output. A status command can end with OK while the connection detail printed just above it says Invalid JWT. Read the connection block, not the final line. That's the whole difference between checking that configuration exists and checking that it works.
Memory gets built asynchronously
Honcho won't hand you a mature profile right after the first message. Hermes writes conversations asynchronously, Honcho's deriver processes them in the background, and peer cards, conclusions, and summaries all need messages to observe first. A brand new system will connect perfectly and still return nothing useful.
That's not a failure. It's just empty.
The distinction I use:
- Connection failure: invalid token, unreachable endpoint, API error
- Processing failure: messages arrive, but the deriver or the model calls fail
- Fresh memory: connection works, there just isn't enough history yet
- Working recall: a new session retrieves a fact from an older one
Only that last one proves the feature I care about. The best smoke test is deliberately boring:
- Tell Hermes one harmless, specific preference.
- End the session.
- Start a fresh session.
- Ask Hermes to retrieve it with
honcho_search.
Don't test with something that already lives in USER.md, MEMORY.md, a repo instruction file, or the current prompt, or you'll get a correct answer for the wrong reason.
How Hermes uses the memory
Hermes runs Honcho in three recall modes.
hybrid injects standing context automatically and hands the model the Honcho tools. It's the default, and mine. Continuity without turning every message into a manual search.
context injects context but withholds the tools. Simpler and more predictable, at the cost of deeper on-demand recall mid-task.
tools injects nothing and lets the model call Honcho when it decides it needs memory. Keeps prompts lean and makes retrieval visible, but it leans on the agent noticing that history matters in the first place.
Hermes exposes four tools, and they do genuinely different jobs:
| Tool | Layer | Use it when |
|---|---|---|
honcho_profile |
Peer card | You want cheap standing context about who this person is |
honcho_search |
Semantic search | You need the actual excerpt, like the exact command we settled on |
honcho_context |
Dialectic Q&A | You want a synthesized answer, like how I prefer technical feedback |
honcho_conclude |
Durable facts | Something learned this session should outlive it |
If I need the exact command we agreed on, that's search. If I want a synthesis of a preference, honcho_context fits better. And if I need one stable fact available cheaply in every single conversation, it belongs in the profile or in an explicit memory file, not in a retrieval call.
Sharing one backend with Claude Code
Yes, you can, and this turned out to be the most useful part of the whole setup.
Honcho ships a Claude Code plugin that talks to the same self-hosted backend. It inherits nothing from your Hermes configuration. It reads ~/.honcho/config.json, and the workspace and peer names are yours to pick:
{
"apiKey": "<your self-hosted JWT>",
"peerName": "hauke", // shared user identity
"hosts": {
"claude_code": {
"workspace": "hermes", // NOT the default "claude_code"
"aiPeer": "claude"
}
},
"endpoint": {
"baseUrl": "https://honcho.example.com/v3"
},
"sessionStrategy": "per-directory"
}Two details do the real work here.
The workspace has to match, and the defaults won't do it for you. The plugin defaults to a claude_code workspace, while Hermes writes to whatever you named during its setup. Leave both at their defaults and you end up with two perfectly healthy integrations that share absolutely nothing. I pointed Claude Code at the hermes workspace on purpose, and that single line is what makes the memory common.
Then there's observationMode, which decides whether the agents stay distinct. The default, unified, pools observations together. Set it to directional and each AI peer keeps its own view of you, so Claude's observations stay with Claude and Hermes' stay with Hermes:
{ "observationMode": "directional" }That produces the architecture I wanted:
┌── Hermes AI peer
Hauke user peer ────┤
└── Claude AI peer
inside one deliberate workspaceOne shared user identity, a separate representation per agent. Common facts about me travel between them without pretending Hermes and Claude are the same thing.
Two smaller things, each of which cost me a restart. The plugin reads apiKey from the config file, and while HONCHO_API_KEY works too, changing it means restarting Claude Code. And the endpoint here needs the /v3 suffix that Hermes specifically doesn't want.
One last thing: don't claim two agents share memory until you've written a fact through one and read it back through the other. Matching JSON files aren't a test.
What I'd do differently
I'd verify the system in layers, instead of configuring the whole thing and then asking why it remembers nothing. My order now:
- Start PostgreSQL, Redis, the API, and the deriver.
- Check container health and deriver logs.
- Verify the Honcho API locally over plain HTTP.
- Add HTTPS and
AUTH_USE_AUTH=true, but only if remote access is actually required. - Mint a token and prove it with
curlagainst/v3/workspaces/list. - Only then configure Hermes with the server base URL.
- Read the full connection block from
hermes memory status. - Write one harmless test fact.
- Retrieve it from a fresh session.
- Add the second client, matching the workspace name deliberately.
- Only then start tuning recall modes, reasoning depth, and observation mode.
This keeps each failure small. Every step I skipped on the first attempt is a step that later cost me an hour of guessing which layer was broken.
I'd also start with managed Honcho if I were evaluating the idea rather than the infrastructure. Self-hosting before proving the memory improves my work would be classic developer behavior: building a small distributed system to avoid filling out one signup form.
Is self-hosting worth it?
For most people trying Hermes, not on day one.
Managed Honcho takes the API, database, vector extension, worker, model configuration, authentication, backups, and upgrades out of the experiment entirely. That's a much cleaner way to answer the first question, which is simply whether long-term agent memory helps you at all.
Self-hosting starts making sense when:
- conversation history is sensitive
- you want control over retention and backups
- multiple agents should share one memory backend
- you already operate the infrastructure it needs
- you want to pick the inference provider
- the system has become valuable enough to own
That third one was my actual reason. Two agents, one memory, one identity.
The tradeoff is straightforward enough. I get control over durable memory, and I take on responsibility for keeping the memory pipeline alive.
That last word is the one that matters. Persistent AI memory isn't a JSON file with better branding, it's a pipeline: ingestion, storage, background extraction, embeddings, retrieval, context assembly, and model inference. Every stage fails independently, and most of them fail quietly.
But when it works, the agent stops feeling like a clever stranger I have to brief every morning. It remembers the kind of software I build, the conventions around my projects, and the way I like to work. Not perfectly, and not magically. Just enough that the next conversation starts somewhere better than zero.
That's worth a little infrastructure.
Just maybe not before coffee.