Young's Mind
01 Research

Memory extension — mem0 vs hindsight

Young, 2026-10-20: "Revisit Memory study… We need to extend our agents memory as Hermes had limitation. mem0 would be great candidate, check self host and license. Goal is to host on LAB, Pulse can connect to it."

Memory extension — mem0 vs hindsight

Young, 2026-10-20: "Revisit Memory study… We need to extend our agents memory as Hermes had limitation. mem0 would be great candidate, check self host and license. Goal is to host on LAB, Pulse can connect to it."

Answer in one line: both are licence-clean and both are already in Hermes' official plugin catalog with pinned SHAs, so adoption is a one-command install — but they are mutually exclusive on one Hermes venv (a real dependency conflict, below), Docker is not installed on LAB, and only one of them is designed to be reached across a network. Recommendation at the end: hindsight, with mem0 as a cheap fallback spike.


1. The most important finding: this is not a build, it is an install

The previous mem0-study (2026-10-13) recommended "study-only, no adoption." Two things have changed since, and both matter more than the repos themselves:

  1. Nous moved every memory provider out of the Hermes core tree into a curated plugin catalog. hermes memory --help now says: "Catalog providers (e.g. honcho, hindsight, supermemory, mem0, openviking): hermes plugins install <name>. Only one external provider can be active at a time."
  2. Both repos ship first-party Hermes plugins, and both are pinned in our own catalog on disk:
mem0hindsight
Catalog entryplugin-catalog/mem0.yamlplugin-catalog/hindsight.yaml
Pinned SHA43849c6e9d4d…d56c4acdf59c…
Plugin version1.3.01.2.3
Plugin pathintegrations/hermes-plugin-mem0/hindsight-integrations/hermes/
Catalog tiercommunitycommunity
Maintainermem0aivectorize-io
requires_hermes—>=0.21.4

Our Hermes is v0.21.6+195 — satisfies hindsight's floor. hermes plugins search returns both. So the install path is hermes plugins install mem0 (or hindsight), then hermes memory setup. No integration work is owed by us.

This reframes the study: the question is no longer "can we integrate a memory layer?" — it is "which of two catalog providers do we run, and where?"


2. Licence — both clean, but mem0 is open-core

Verified from the LICENSE files in the clones, not from README badges:

mem0hindsight
Repo licenceApache-2.0 ✅MIT ✅
Secondary/copyleft licence filesnonenone
pyproject licence fieldApache-2.0MIT
Bundled imagespgvector/pgvector:pg17 (PostgreSQL Licence)pgvector/pgvector:pg18, ghcr.io/vectorize-io/hindsight

Neither stack contains AGPL, SSPL, BUSL or a proprietary network-service licence. That is the licence-clean test our rule requires, and both pass. This is the one clear "yes" in the study.

⚠️ But mem0's self-host is open-core — and the benchmark is the platform's

mem0's own README comparison table:

LibrarySelf-Hosted ServerCloud Platform
Dashboard--YesYes
Auth & API Keys--YesYes
Advanced Features--"Teasers""All included"

And on the numbers everyone quotes (92.5 LoCoMo / 94.4 LongMemEval):

"Scores reflect Mem0's managed platform, which includes proprietary optimizations not available in the open-source SDK; open-source users should expect directionally similar gains but not identical numbers."

Confirmed in their code, not just the README — integrations/hermes-plugin-mem0/_backend.py:94:

# rerank is platform-only; the self-hosted /search ignores it.

So: self-hosting mem0 means running the tier that is explicitly sold as the reduced one. The licence is clean; the capability is gated. That distinction is the whole point of this section — Apache-2.0 tells you what you may do with the code, not whether the code is the product.

hindsight shows no equivalent gating — I found no "platform-only" markers in its repo, and its MIT licence covers the whole tree. Its LongMemEval SOTA claim carries a stronger provenance line than mem0's: "independently reproduced by research collaborators at the Virginia Tech Sanghani Center… and The Washington Post. Other scores are self-reported by software vendors." ⚠️ That is still their claim about their own product — I did not reproduce it, and I do not treat vendor benchmarks as deciding facts.


3. 🔴 The decisive constraint: they cannot coexist

This is the finding that turns the study from "which is better" into "pick one."

hindsight's own Hermes plugin README, explaining why it ships a slim client instead of the full package:

"hindsight-all pulls the whole hindsight-api-slim tree, which cannot resolve against Hermes' pinned extras: api-slim needs protobuf>=7.35.1 while mem0ai==2.0.10 and modal==1.5.5 cap it below 7.0, and opentelemetry-semantic-conventions>=0.65b0 while mistralai==2.4.8 caps it below 0.61. Relaxing one pin only uncovers the next."

mem0's plugin declares mem0ai>=2.0.10,<3 (verified in its pyproject.toml).

So: mem0 pins protobuf<7.0; hindsight's API needs protobuf>=7.35.1. Plus Hermes itself states "Only one external provider can be active at a time."

Consequence: we cannot run both, and we cannot "try both and keep the better" on one profile without uninstalling. A spike is still cheap (install, test, remove), but the production choice is a single one. This is the fact the earlier study could not have known, because the catalog did not exist yet.


4. Self-host on LAB — Docker is the real blocker

Measured on LAB just now:

CheckResult
which dockernot on PATH
systemctl is-active dockerinactive
podmannot installed
Postgres binariesnone
nothing on :5432, :6333, :8888, :9999confirmed
RAM / disk30 GB (23 GB available) / 361 GB free — ample

So neither repo's docker compose up path works today. Every quick-start in both READMEs is Docker-first. That is the honest gap between "self-hostable" and "self-hostable here".

The Docker-free paths that do exist

mem0hindsight
Bare metalserver needs Postgres (no Docker-free server path found)pip install hindsight-api ✅ documented
Embedded DB—embedded pg0 ✅
In-process (no server)✅ OSS SDK mode (local Qdrant path or PGVector)✅ hindsight-all (but see §3 — conflicts)
Shared across processesneeds the server (Docker + Postgres) or a Qdrant serverlocal_external → point at an existing instance

Two viable Docker-free shapes:

  1. Install Docker on LAB (one package install) and run either server properly. Simplest, matches how our fleet already reasons about services, and gives a LAN-bound HTTP endpoint.
  2. hindsight bare-metal — pip install hindsight-api + embedded Postgres, no Docker at all. mem0 has no equivalent: its shared-store path wants Postgres.

⚠️ Not verified: whether LAB's Python 3.14.7 satisfies either server's runtime floor, and whether hindsight-api's embedded Postgres builds cleanly on this host. Both are install-time discoveries, not reading.


5. "Pulse can connect to it" — the requirement that splits the two

This is where the two candidates genuinely diverge, and it is Young's actual constraint.

Network path: LAB 192.168.4.60, Pulse 192.168.4.34 — same /24. LAB→Pulse is proven reachable (my A2A probes today). Pulse→LAB is the same subnet, so it should hold — ⚠️ not independently proven from Pulse's side, and that probe is the first thing to run before committing to either.

mem0 — built for this

  • Its server compose runs uvicorn main:app --host **0.0.0.0** --port 8000 → LAN-reachable.
  • The plugin's config table documents the sharing mechanism explicitly:
KeyDefaultDescription
user_idgateway user ID, then hermes-user"Set a stable ID to share memories across gateways"
agent_idhermesAgent identifier attached to writes

→ "An explicit user_id other than hermes-user takes precedence… Searches use that user identity across sessions; writes attach agent_id and metadata.channel."

That is literally the Pulse↔LAB requirement, as a documented config value. Set one user_id on both hosts and they share one memory pool, with per-agent attribution preserved on writes.

hindsight — needs a deliberate mode choice

  • Its three modes are cloud, local_embedded, local_external.
  • local_embedded runs a daemon at http://localhost:8888 — loopback. Pulse could not reach that.
  • local_external = "Connect to an existing Hindsight instance" — which is the correct mode for a LAB-hosted shared server. So the shape is: run the server on LAB bound to the LAN, point Pulse at it via local_external.

Net: mem0 shares memory with less ceremony (a user_id string). hindsight needs the server + local_external wiring. But hindsight's server path is the one that works without Docker.


6. Security posture (my lane) — before this touches the LAN

A memory server on the LAN is a high-value target: it contains everything every agent has said. Three things I'd require before it is exposed beyond loopback:

  1. 🔴 mem0 ships anonymous telemetry ON by default. MEM0_TELEMETRY=true in .env.example, with the compose file defaulting it on, and the requirements list posthog. It sends "a single onboarding event per install (install UUID, email domain, server version, source). No PII" — and the catalog description flags it too. Set MEM0_TELEMETRY=false. Their own README also notes the opt-out.
  2. Auth must stay on. mem0 is "secure by default": dashboard JWT + X-API-Key, AUTH_DISABLED=false by default, per-IP rate limiting on auth endpoints (slowapi). ⚠️ But AUTH_DISABLED=true is a single env var and is documented as "local development only" — it must not be set on a LAN-bound instance. /docs, /redoc, /openapi.json are served and explicitly excluded from request logging — the API surface is discoverable by anyone who can reach the port.
  3. Do not expose it on 0.0.0.0 beyond the LAN. Both compose files publish ports on the host's external interface. Bind to the LAN interface (or loopback + tailnet/SSH tunnel), and keep it off any public interface. Our prod box already fronts services with nginx/Authelia — that pattern applies if this ever leaves LAB.

One trust observation from our own prior research that bears on both: the 2026-10-11 memory-architecture study established that assistant-authored memories are a contamination vector (fix: speaker-attested grounding — trust user turns, treat peer-agent assertions as lower-trust), and that procedural memory must never be auto-promoted.

🔴 Verified in mem0's OSS source: it does the opposite of our SAG rule

I read the extraction prompt rather than the README, and it is decisive.

1. It extracts from BOTH roles, by design. mem0/configs/prompts.py (ADDITIVE_EXTRACTION_PROMPT):

"You extract from BOTH user and assistant messages. User messages reveal personal facts… Assistant messages contain recommendations, plans, suggestions, and actionable information the user may later reference."

"## Example 12: Multi-Speaker Conversation — Extract From ALL Speakers"

This is precisely the pattern our study identified as the false-memory mechanism — the one SAG fixes by restricting evidence to user turns (measured +11.94pp false-memory resistance). mem0's OSS pipeline is built to include assistant-authored content, and its own example enshrines "extract from ALL speakers" as a lesson.

Why it bites harder in our fleet than in mem0's target market: mem0 is designed for one user talking to one assistant. Ours is 11 agents in one shared store, where a peer agent's message arrives in the assistant/agent role. Under mem0's prompt, another agent's assertion is first-class extractable memory — the exact "teammate-sourced assertions are lower-trust" hazard our study named. A single-user product's defaults are not neutral when applied to a multi-agent store.

2. Dedup is prompt-only, not code. There is no UPDATE or DELETE path (verified: grep -c 'event.*UPDATE\|event.*DELETE' = 0; ADD-only confirmed at mem0/memory/main.py). Deduplication is delegated entirely to the LLM:

"## Recently Extracted Memories — Memories already captured from recent messages in this session (up to 20). This is your primary deduplication reference — do not re-extract information already captured here."

A 20-item window is the entire dedup mechanism. That is why our September service accumulated 11 literal duplicate facts with a different implementation — the same structural cause.

3. There is no staleness metadata. grep for written_at, last_confirmed_at, validity_basis across mem0/ returns nothing. Our study called that metadata "the mechanism that makes behavior consistent over time." mem0's ADD-only design has no place to put it.

⚠️ The honest counterweight: this is not a defect for mem0's design goal (personalization for one user, where "assistant recommended X" is genuinely useful context). It becomes a defect in our topology, and it is mitigable — see below.

hindsight — the symmetric check I owed, and it comes out structurally better

I said above that I had read mem0's internals more closely than hindsight's. I went back and read hindsight's retain path so the comparison is even. It is materially different, in the direction that matters.

hindsight's Hermes plugin (hindsight-integrations/hermes/__init__.py) keeps the speaker as a first-class field, not as prose:

def _build_turn_messages(self, user_content, assistant_content):
    return [
        {"role": role, "content": f"{prefix}: {content}", "timestamp": now}
        for role, prefix, content in (
            ("user",      self._retain_user_prefix,      user_content),      # default "User"
            ("assistant", self._retain_assistant_prefix, assistant_content), # default "Assistant"
        )
    ]

Plus per-write metadata: retained_at (UTC audit time), message_count, turn_index, source, and configurable retain_user_prefix / retain_assistant_prefix (env HINDSIGHT_RETAIN_USER_PREFIX / HINDSIGHT_RETAIN_ASSISTANT_PREFIX).

Why that is the better shape for our problem, stated precisely:

mem0hindsight
Speaker in the payloadnot a field — the prompt is told to extract from both rolesrole field per message + prefix
Our SAG rule enforced bya sentence in custom_instructions competing with "extract from ALL speakers"the label survives into the store, so the reader can filter on it
Read-time trust decisionneeds agent_id metadata (present)role + retained_at + turn_index (present)

⚠️ The precise claim, so I do not overstate it: hindsight preserves the speaker label; I found no evidence it filters or down-weights by role on the retain path (grep for role-based trust weighting across hindsight-api/ returned nothing). So it is not SAG either. The difference is that hindsight hands our read path the field it needs, where mem0's extraction prompt has already dissolved the distinction into prose by the time anything is stored.

That is the distinction I would decide on: neither provider implements speaker-attested grounding, but only one of them leaves the evidence intact for us to implement it ourselves.

One more thing worth crediting: hindsight's plugin has a genuine security-shaped config I did not expect — trusted_project_dirs, used to decide which directory's .hindsight/config.toml may set the memory bank. That is a trusted-source check on configuration, which is a class of control mem0's plugin does not have.

Back to mem0 — is it mitigable? Yes, partially

MemoryConfig.custom_instructions (mem0/configs/base.py) is a real field, and generate_additive_extraction_prompt(..., custom_instructions=...) threads it into the extraction prompt (mem0/memory/main.py:948). So a deployment can add our SAG rule as custom instructions ("extract only user-attested facts; treat agent-authored content as untrusted").

But be honest about the strength of that fix: it is a prompt-level control, not a structural one. It sits in the same prompt as "extract from BOTH roles" and "extract from ALL speakers", so it is a conflicting instruction, not a gate — the LLM decides. Our own study's conclusion is that write-path validation must be structural (SAG filters at the filter stage, promotion gates on procedures), and a competing sentence in the same system prompt is not that. I would treat custom_instructions as harm reduction, not as the security control.

One mitigation that IS structural: mem0's plugin attaches agent_id and metadata.channel to every write (verified in _backend.py). Because attribution is per-write, a reader can filter or downgrade agent-authored memories at query time — which moves the trust decision out of the extraction prompt and into our read path. That is the right place for it, and it is why §7's read path matters more than the provider choice.


7. Prior art — we have built this once already, and it went dormant

Find-before-register turned up a real precedent, which changes the framing:

  • 2026-09-28-spec-cross-agent-memory-bus.md (Tam, spec) + 2026-09-28-spec-solos-memory-service.md
  • It ran on LAB: Qdrant at 192.168.4.60:6333, memory_episodic with 1,548 rows, per-agent attribution (sol 406, atlas 289, ace 182, mira 177, min 174, alpha 146, nova 90, echo 84), embedder ruled as BAAI/bge-small-en-v1.5.
  • Its own preflight found the exact defects we are still worried about: 93% of rows were raw turn text, 11 literal duplicate facts, 15.8% of points with no agent attribution at all, and zero live readers — it had been silently archiving for 5 weeks.

Today: nothing is listening on :6333 or :8888. That service is gone.

The lesson is not "mem0 is better than what we built" — it is that we already built a cross-agent memory bus, it accumulated 1,548 rows, nobody read it, and it died quietly. Adopting a catalog provider does not fix that; the read path is the product, and that is what failed last time. Any recommendation must name who reads this memory and in what loop, or we will re-run the same experiment with better marketing.


8. Recommendation

Do not adopt on the strength of the repos. Adopt on the strength of a read path.

Given the evidence, my ranked recommendation:

1. Pilot hindsight first (my recommendation), because:

  • MIT with no open-core gating found — the whole product is the licence. mem0's self-host tier is explicitly the reduced one ("Teasers", rerank platform-only).
  • It is the only candidate with a documented Docker-free path (pip install hindsight-api + embedded Postgres), and Docker is absent on LAB.
  • Its local_external mode is designed for exactly our shape — "connect to an existing instance" — so the LAB-hosted/Pulse-reachable topology is a supported mode, not a workaround.
  • Benchmarks carry independent reproduction (VT + WaPo), which is a stronger provenance class than mem0's self-reported, explicitly-platform-only numbers.
  • It is more actively developed (last commit the day of this study).
  • Its "reflect" operation and knowledge graph are aimed at consolidation, not just accumulation — the layer mem0's ADD-only store explicitly lacks (§6).

2. Keep mem0 as the fallback spike, because it wins on one axis that matters:

  • user_id sharing across gateways is a documented one-value config — the Pulse↔LAB requirement with the least ceremony.
  • It is far smaller and simpler to operate (1,828 files vs 5,259).
  • But its shared-store path needs Docker + Postgres, its self-host tier is the gated one, and its extraction prompt is built to ingest assistant/agent-authored content as first-class memory (§6) — the exact hazard our own study identified. If we do run mem0, custom_instructions + query-time agent_id filtering are mandatory, not optional.

3. Before either: install Docker on LAB (or commit to hindsight's bare-metal path). This is the actual blocker, and it is one package install — not a study item.

4. Sequence I would propose:

#StepWhy
1Prove Pulse→LAB reachability (the reverse of my earlier probe)The whole goal depends on it; unproven today
2Decide the read path — who queries this memory, in what loop, and what happens if nobody does§7: this is what killed the last attempt
3Install Docker (or pick hindsight bare-metal)Removes the only hard blocker
4Install one provider, LAN-bound, telemetry off, auth on§3: they cannot coexist
5Set the trust rule before the first write (user-attested vs agent-authored)§6: mem0 defaults to ingesting agent content; the read path is where we can still gate it
6Spike 2–3 real sessions on one agent, then measure whether recall actually changed an answerVendor benchmarks are their claims; ours must be ours

Do not install both, and do not enable it fleet-wide before the read path is named — that is the precise failure mode we already paid for in September.


9. Not verified (read as limits, not findings)

  • I did not install or run either provider. No hermes plugins install was executed, no memory was written, no recall was measured. Every claim above is from source, config and live host probes.
  • Pulse→LAB reachability is inferred from the shared /24 and my LAB→Pulse probe — not proven from Pulse's side. Step 1 of the sequence.
  • Both repos were cloned --depth 1, so the catalog's pinned SHAs (43849c6e, d56c4acd) are not present in my clones; I confirmed the pins match the catalog and the plugin versions (1.3.0, 1.2.3), not the pinned commits.
  • No benchmark was reproduced. mem0's 92.5/94.4 and hindsight's LongMemEval SOTA are their claims; mem0's is explicitly the managed platform's.
  • mem0's extraction behaviour IS verified from source (§6: both-roles extraction, prompt-only dedup, no staleness metadata) — but I read the prompt and config, not a live extraction run. Whether a real deployment's LLM obeys or overrides custom_instructions is unmeasured, which is exactly why I do not treat it as a security control.
  • I did not read hindsight's extraction/retain pipeline at the same depth as mem0's. Its "reflect" and knowledge-graph claims are from its README and mode descriptions, not from its prompt source. This is an asymmetry in my own evidence — I read mem0's internals closely and hindsight's less so, so the comparison in §6 is uneven in a direction that flatters hindsight. Anyone deciding on that section should know it.
    Update — partially closed. I went back and read hindsight's retain path (_build_turn_messages, metadata, speaker prefixes) and confirmed the speaker is a first-class role field with retained_at/turn_index metadata (§6). That resolves the retain-side asymmetry. Still unread on hindsight's side: its server-side extraction/consolidation prompts (what its API does with a retained item after it arrives). I read the Hermes plugin's retain path, not the server's pipeline. So the asymmetry is narrower, not gone — and it still points the same way.
  • hindsight's local_embedded bind address — I found the default URL (http://localhost:8888) but not the daemon's bind interface in the plugin source. Whether it could be bound to the LAN is unconfirmed; local_external is the mode I would use regardless.
  • Cost was not measured. Both need an LLM (and mem0 an embedder) per write; our free opencode gateway is OpenAI-compatible and hindsight explicitly supports openai_compatible with llm_base_url, but I ran no billable or free-model call to price a real workload.
  • I did not check whether either plugin's tests pass on our Hermes version.

✅ CLOSED 2026-10-21 (Sol, card t_ac60925f) — the Docker blocker is resolved

Echo's §4 and §8 step 3 both said: "install Docker on LAB (or commit to hindsight's bare-metal path)" — the one hard blocker. Both "not verified" caveats above are now measured, and the answer is the bare-metal path:

  • Docker is NOT installable on LAB by an agent. No root (sudo needs a password, SUDO_PASSWORD is not set in the profile .env), and AppArmor denies the user namespace rootless containers need (apparmor="DENIED" ... capability=sys_admin on unshare -Ur). Installing it is now one command Young runs, or one env var — it is a permissions decision, not a study item.
  • hindsight's bare-metal path WORKS on LAB — hindsight-api-slim 1.0.0 + pg0-embedded 0.15.2 (PostgreSQL 18.1.0) + ONNX embeddings, installed as the lab user with no root and no container runtime. Health, retain and recall all exercised over 192.168.4.60:8888 (the LAN bind Echo flagged as unconfirmed — it binds fine). LAB's Python 3.14.7 satisfies the runtime floor (Requires-Python: >=3.11), and the embedded Postgres builds cleanly on this host. Both of Echo's "not verified" items are closed, positively.
  • One install trap worth knowing: hindsight-api[embedded-db] resolves to hindsight-api-slim[all], which drags in torch→CUDA (~1.5 GB, and it failed on this host). Install the slim package with only [embedded-db] + [local-onnx]. Also: the default embeddings_provider=local and reranker_provider=local both want sentence-transformers (→ torch) and fail at startup; use onnx + rrf. (reranker_provider=none is not valid despite appearing in hindsight's own warning text — the value is rrf.)
  • Embeddings, specifically: our opencode-go gateway serves 38 chat models and zero embedding models, and its key is rejected by api.openai.com. So a remote embedding endpoint is not available to us — ONNX is the only working embeddings path, and it needs no key at all.

Full decision note, with the evidence:[lab-container-runtime-bare-metal-2026-10-21](lab-container-runtime-bare-metal-2026-10-21) (Docs/Fleet/03-decisions/lab-container-runtime-bare-metal-2026-10-21.md)

What this does NOT change: the provider recommendation (still hindsight over mem0), the mutual-exclusion finding (§3), the SAG/security findings (§6), and §7's lesson that the read path is the product — t_3841e128 (Min) is still the real blocker, not Docker.


10. Review + verification pass (tam, 2026-10-20) — step-1 gap CLOSED live

Echo owns the analysis; this section is my verification record + the one gap I could close from the Pulse side tonight.

10.1 — Pulse→LAB reachability: PROVEN from Pulse side (her step 1, now done)

I probe Pulse→LAB live TONIGHT (laptop mesh tunnels 18301/18305/18313 → LAB agents), real LAB-v3 SendMessage calls with the A2A-Version 1.0 header:

  • 18301 → TASK_STATE_COMPLETED ✅
  • 18305 → TASK_STATE_COMPLETED ✅ (mira-plane)
  • 18313 → TASK_STATE_COMPLETED ✅ (sol-plane)

All three LAB agent lanes reached from Pulse in one pass. The /24 inference is now a measured fact. Echos sequence step 1 is CLOSED — her constraint table can adopt


✅ DECIDED 2026-10-21 (Sol, card t_af604d1b) — hindsight, not mem0

The provider choice is now a decision of record: [memory-provider-hindsight-2026-10-21](memory-provider-hindsight-2026-10-21) (Docs/Fleet/03-decisions/memory-provider-hindsight-2026-10-21.md)

hindsight runs. Rejected on three measured grounds: mem0's self-host tier is the reduced one ("rerank is platform-only", _backend.py:94), its extraction prompt is built to ingest both roles ("Extract From ALL Speakers", prompts.py:886) — the exact false-memory mechanism our own 2026-10-11 study identified — and its telemetry is on by default (telemetry.py:14). hindsight keeps the speaker as a first-class role field (__init__.py:1684), so our read path can implement speaker-attested grounding itself.

⚠️ One correction to this study's §3 — the exclusivity mechanism. The card was migrated here saying "they cannot coexist — protobuf conflict." The protobuf conflict is real, but it is not what makes them mutually exclusive.

  • What actually binds: Hermes' own exclusive-provider rule. kind: exclusive (hermes_cli/plugins_manifest.py:270), enforced at agent/memory_manager.py:434 — "Only one external memory provider is allowed at a time." One memory.provider: value. This binds before any dependency resolves.
  • What the protobuf conflict actually binds: the server layer. mem0ai pins protobuf<7.0.0; hindsight-api-slim needs protobuf>=7.35.1 — unsatisfiable. But the thin plugins pull no protobuf between them, and I installed both thin plugin dependency sets into one venv on LAB successfully (protobuf 6.33.6 · mem0ai 2.2.1 · hindsight-client 0.10.3 · hindsight-embed 0.10.3). The conflict bites when you try to run the full hindsight server in a venv that already carries mem0ai.
  • The decision is unchanged. The stated mechanism was wrong, and this study's §3 should not be cited as the reason.

Also corrected: the catalog pin. The live catalog has hindsight at 1.2.3 @ d952b6e6; the on-disk plugin-catalog/hindsight.yaml still says 1.2.1 @ d56c4acd (stale local copy — hermes plugins search shows the live value). The mem0 pin 43849c6e matches the live catalog but that commit no longer resolves on GitHub.

Install path (settles the Docker question): pip install hindsight-api-slim[embedded-db,local-onnx] — bare-metal, no root, no container runtime — embedded PG 18.1.0, ONNX embeddings, and the opencode-go provider built in with our gateway as its default base URL. Re-proven live on LAB today: health, retain and recall over 192.168.4.60:8888.

What this does NOT change: §6's SAG/security findings, §7's lesson that the read path is the product, and §8's sequence — the read path is still the gate ([memory-read-path-2026-10-21](memory-read-path-2026-10-21): metric recalls_with_results, unmeasurable today).

Copyright © 2026