10 KiB
description, icon
| description | icon |
|---|---|
| Optional `Memory` trait backend that delegates to a locally-running agentmemory REST server, for users who self-host agentmemory across Claude Code, Cursor, Codex, OpenCode, and OpenHuman. | database |
agentmemory backend
OpenHuman's default Memory trait backend is sqlite, the unified store
documented in Memory Trees. Some users already
self-host agentmemory, typically
because they want a single durable memory shared across Claude Code,
Cursor, Codex, OpenCode, and OpenHuman. For them, OpenHuman exposes an opt-in
backend that proxies every trait call through agentmemory's REST surface.
Selecting backend = "agentmemory" skips OpenHuman's SQLite + embedder
path entirely. agentmemory owns the storage, embedding, and retrieval
layers. OpenHuman becomes a thin REST client.
When to use this
Use the agentmemory backend if:
- You already run
npx -y @agentmemory/agentmemoryfor one or more coding agents and want OpenHuman to share the same durable store. - You want hybrid BM25 + vector + graph retrieval without provisioning a separate embedder on the OpenHuman side.
- You prefer agentmemory's lifecycle (consolidation, retention scoring, auto-forget, graph extraction) over OpenHuman's unified store.
Keep the default sqlite backend if:
- You want self-contained, single-process operation with no external daemon dependency.
- You rely on OpenHuman-specific Memory Tree features (chunking, sealing, summary trees) that operate on top of the SQLite store. The Memory Tree pipeline is unaffected by the trait backend, since it operates on the host's document store independently. Even so, the agentmemory backend is most valuable when you've already standardised on agentmemory across other agents.
Quick start
-
Install + start agentmemory (one terminal):
npx -y @agentmemory/agentmemoryDefaults to
http://localhost:3111(REST) +ws://localhost:49134(engine). First boot generates an HMAC secret at~/.agentmemory/.hmacand prints it once. -
Point OpenHuman at it in your
config.toml:[memory] backend = "agentmemory" # Defaults below — set only when overriding. # agentmemory_url = "http://localhost:3111" # agentmemory_secret = "" # HMAC bearer token, optional # agentmemory_timeout_ms = 5000 -
Restart OpenHuman. The factory short-circuits the SQLite path and logs
[memory::factory] using agentmemory backend at <url>.
That's it. Existing OpenHuman call sites (store, recall, get,
list, forget, namespace_summaries, count, health_check) work
unchanged.
Config keys
| Field | Default | Purpose |
|---|---|---|
agentmemory_url |
http://localhost:3111 |
Base URL for the agentmemory REST server |
agentmemory_secret |
none | Optional HMAC bearer token. Sent as Authorization: Bearer <secret> |
agentmemory_timeout_ms |
5000 |
Per-request reqwest timeout |
When backend == "agentmemory", the following existing MemoryConfig
fields are ignored, because agentmemory owns its own embedding stack
via ~/.agentmemory/.env:
embedding_providerembedding_modelembedding_dimensionssqlite_open_timeout_secs
Setting them on this path is a no-op. The local-AI Ollama health-gate also doesn't run on this path, because agentmemory's daemon manages its own embedder lifecycle.
Field mapping
OpenHuman's MemoryEntry ↔ agentmemory wire row:
| OpenHuman field | agentmemory field | Notes |
|---|---|---|
namespace |
project |
Defaults to "default" when empty |
key |
title |
|
content |
content |
|
id |
id |
agentmemory-generated (mem_<rand>) |
category: Core |
type: "fact" |
|
category: Daily |
type: "conversation" |
|
category: Conversation |
type: "conversation" |
|
category: Custom(s) |
type: "fact" + concepts: [s] |
Custom tag rolled into the concepts array so it remains queryable |
session_id |
sessionIds: [...] |
OpenHuman exposes a single id; agentmemory persists an array |
timestamp |
updatedAt (RFC3339) |
Falls back to createdAt if updatedAt is absent |
score (recall hits only) |
smart-search score |
Populated on recall responses, None on get / list |
agentmemory carries additional fields that this backend leaves at
defaults: concepts (auto-extracted), files (path tags), strength
(retention score), version, and supersedes (the lifecycle chain).
They're internal to agentmemory's lifecycle layer and don't need to
round-trip through OpenHuman's trait.
Trait method → endpoint
Memory method |
agentmemory REST | Notes |
|---|---|---|
store |
POST /agentmemory/remember |
{project, title, content, type, concepts, sessionIds} |
recall |
POST /agentmemory/smart-search |
Hybrid BM25 + vector + graph |
get |
POST /agentmemory/smart-search |
+ client-side exact-title filter |
list |
GET /agentmemory/memories?latest=true&project=<ns> |
|
forget |
get(ns, key) → POST /agentmemory/forget |
Two-step: resolve id then forget |
namespace_summaries |
GET /agentmemory/projects |
Returns [{name, count, lastUpdated}] |
count |
GET /agentmemory/health |
Reads memories field |
health_check |
GET /agentmemory/livez |
RecallOpts.category, RecallOpts.session_id, and RecallOpts.min_score
are applied as client-side filters on the smart-search response.
agentmemory's REST surface doesn't expose them as server-side filters
today. For very large recall windows (limit > 100) prefer issuing a
tighter query string to reduce server-side work over relying on
client-side post-filtering.
Security
When agentmemory_secret is set, the client honours agentmemory's
v0.9.12 plaintext-bearer guard contract:
- Loopback hosts (
localhost,127.0.0.1,::1) overhttp://are allowed. This is the local dev path. https://to any host is allowed.- Plaintext HTTP to a non-loopback host emits a one-time stderr warning at construction time. The bearer is observable on the wire.
AGENTMEMORY_REQUIRE_HTTPS=1(process env, ASCII-case-insensitive matches1ortrue) escalates the warning into a hard refusal at client construction. The backend fails to start rather than leak the bearer once.
Production deploys should set AGENTMEMORY_REQUIRE_HTTPS=1 so a
misconfigured TLS terminator fails loud rather than silently leaking.
The plaintext-bearer guard mirrors the integration plugin guards in agentmemory's PR #315 so an operator who's seen the warning on Hermes / OpenClaw / pi will recognise the same message on OpenHuman.
Failure modes
| Failure | Backend behaviour |
|---|---|
| Daemon unreachable at startup | from_config succeeds (URL parses), but health_check() returns false on first call. Trait methods bubble up reqwest transport errors |
| Network timeout | anyhow::Error per trait contract; surfaces to caller |
| 4xx / 5xx response | anyhow::Error with status + body snippet |
| Bearer over plaintext non-loopback (no env) | One-time stderr warning, request proceeds |
Bearer over plaintext non-loopback + AGENTMEMORY_REQUIRE_HTTPS=1 |
Hard refusal at construction time |
Empty agentmemory_url |
Hard refusal at construction time with hint to leave it unset for the default |
| Invalid URL syntax | Hard refusal at construction time with the parser error |
No automatic fallback to SQLite. If the daemon is down at boot, the
backend surfaces the transport error loudly. Operators flip back to
backend = "sqlite" in config.toml to recover. Rationale: a silent
SQLite fallback would hide a misconfigured daemon, and "private, simple,
predictable" wins over "magically tolerant".
Performance notes
The backend is a thin REST proxy: it adds one HTTP round-trip per trait call. Practical implications:
storeandforgetare single-RTT.recall,get,listare single-RTT.forgetagainst an unknown key is two-RTT (the implicitgetlookup- a no-op confirmation). Caller can short-circuit this by checking
the return value of a prior
list.
- a no-op confirmation). Caller can short-circuit this by checking
the return value of a prior
- agentmemory's REST is
127.0.0.1by default, so same-host latency is sub-millisecond. Over a managed deploy with HTTPS termination, expect roughly 10 to 30ms per RTT. - The default per-request timeout is 5 seconds. Bump
agentmemory_timeout_msif you're seeing intermittent timeouts on cold-start of the iii engine; agentmemory's first-request latency after a long idle can stretch toward 3 to 5s depending on persistence state.
Migration: from SQLite to agentmemory
There's no in-place migration today. The recommended path:
- Export your existing memories from the SQLite store via OpenHuman's existing export RPC (or by direct SQL).
- Walk the export and POST each row to
/agentmemory/rememberwith the sameproject+title+content. agentmemory will assign new ids; the OpenHuman side picks them up on firstlist. - Set
backend = "agentmemory"and restart.
A dedicated bulk import path is filed as a follow-up.
Implementation reference
In-tree files:
store/agentmemory/mod.rs: module surfacestore/agentmemory/backend.rs:impl Memory for AgentMemoryBackendstore/agentmemory/client.rs: reqwest wrapper + plaintext-bearer guardstore/agentmemory/mapping.rs:MemoryEntry↔ agentmemory JSONtests/agentmemory_backend.rs: 12 axum-mock integration tests
Related upstream:
- agentmemory repo: https://github.com/rohitg00/agentmemory
- agentmemory REST contract:
~/.agentmemory/.envkeys + endpoint list in the agentmemory README - v0.9.12 plaintext-bearer guard: agentmemory PR #315