Coexo/Building Blocks/Memory
Section 12Partial

Memory

Coexo's shared brain. Every surface — Telegram, every coding session, every future agent — reads from and writes to the same canonical store. Owned by Stephan, replaceable underneath, never silently overwritten.

Vision

Memory is the shared brain Coexo lives on. Every interaction — a Telegram message, a coding session, a decision — leaves a trace in one canonical place. Every Coexo surface, on any host, reads and writes through the same door. None of them owns it; Stephan does.

The principle is short. Everything is kept; nothing silently overwritten. The substrate outlives whatever AI is sitting on top this week. Every agent uses the same simple door, so a thing learned in one place is immediately useful in every other.

Implementation

Event log

Append-only Postgres table holding every signal, message, decision, and state change. Stable IDs, bi-temporal validity intervals, source and author-type provenance, importance scores. The canonical truth. v0 schema applied on coexo-memory; tombstone primitives in place; first events written through the operator and round-tripped via similarity recall.

Domain entity tables

First-class objects derived from events — Stephan, the fleet hosts, the projects, the procedures, the concepts — with relationships between them. First-class entity catalogue (`entities` table) and relationship graph (`triples` table) shipped 2026-05-03 in migration `0002_v1_envelope`. Slugged entity IDs (`person:stephan-terhorst`, `host:coexo-memory`), six entity types in the open enum, bitemporal validity, Hebbian recall_count column, tombstone primitives. Populated lazily by the light-phase consolidation worker (Haiku 4.5 extraction); deduped by the REM phase weekly. Substrate today: ~120 entities, ~60 entity-to-entity triples. Per-domain projection tables (cases, tasks, persons, documents) deferred to v2 — added on access-pattern evidence rather than preempted, since the JSONB envelope on `events` already holds the same shape and the entity catalogue handles cross-cutting reference.

MIRIX-type tagging

Every event is labelled with what kind of memory it is, and that label drives what gets shown, retrieved, and forgotten when. Every event carries a `mirix_type` enum — episodic, semantic, procedural, core, resource, vault — set at write time from the URL prefix (`/memories/core/...` → core, `/memories/procedural/...` → procedural, etc.) or defaulted to episodic. Drives the per-type retriever dispatch in `/recall`, the prelude's three-section grouping (core in identity/fleet/preferences, procedural in Skills, episodic+semantic in Recent focus), the active-forgetting eligibility filter (core never forgotten), and the Vault gating policy (vault never returned through any retrieval endpoint).

Hybrid retrieval engine

Recall finds things by words, by meaning, and by relationship — three signals running in parallel, fused, then reranked. Full hybrid stack live as of 2026-05-03: vector + BM25 + RRF fusion + Voyage rerank-2.5 + HippoRAG-2 graph. **`POST /recall`** runs `recall_vector` (Voyage voyage-3-large 1024-dim halfvec, HNSW cosine, mirix_type-filtered, optional time_window_days + min_importance + section) and `recall_bm25` (pg_search per-column @@@ over `events.body_text` via the events_search_idx, paradedb.score(id) as the rank signal) in parallel against the same candidate set, over-fetched at 75 candidates per lane (ZeroEntropy 2025-26 reranker sweet spot). The two ranked lists fuse via Reciprocal Rank Fusion (Cormack 2009, k=60), then the fused pool feeds Voyage rerank-2.5 over the full 32K-context window for the final ordering. Rerank is config-toggleable + fail-soft (RRF order is the fallback). Each hit carries `cosine_similarity`, `bm25_score`, `rrf_score`, `rerank_score` for debug visibility; the response carries a `rank_signal` field. **`POST /recall/graph`** runs the existing HippoRAG-2 PPR over the entities + triples graph with vector-seeded reset weights, Hebbian `recall_count` bump per hit. Eval harness shipped alongside (20-query golden corpus, recall@k + MRR runner under `eval/`); measured Sprint C lift over the vector-only baseline: +20% recall@5, +31.5% MRR. The pivot from the originally-planned Self-RAG retrieval gate happened during research: 2025-26 SOTA (Mem0 arxiv 2504.19413, MIRIX 2507.07957, Anthropic Contextual Retrieval Sept 2024) converged on unconditional hybrid retrieval as the right architecture at our substrate size.

Memory tool API

The single typed contract every Coexo surface uses to read, write, recall, and consolidate. Internal typed Coexo Memory API (recall, event_log, write, consolidate) is the substrate's contract, exposed by the memory operator service. v0 shipped the write and recall paths plus event listing; v0.5 first cut added public-facing `POST /events`, `POST /recall`, `GET /prelude`, and `GET /events`/`GET /events/{id}` over tailnet HTTP, all gated by per-source bearer tokens. The token file (`/etc/coexo-memory/api-tokens.json`) is keyed by SHA256 of the plaintext, so plaintext tokens never live on the operator host; the operator hashes-and-looks-up each request. Each token binds to a single source identity, and the operator overrides client-supplied source / `provenance.api_client` with the token-bound value to prevent spoofing. **`POST /memory` (Anthropic `memory_20250818` six-verb FS) shipped 2026-05-03** as the canonical write contract: view, create, str_replace, insert, delete, rename. The operator returns `{is_error, output, ...}` bodies that callers can pass straight back to Claude as a tool_result block. Internally, every mutation INSERTs a new event AND upserts the `memory_paths` projection in one transaction, so the substrate stays append-only while the agent sees ordinary file semantics. **`POST /events/from-transcript` (write-back hook) shipped 2026-05-03**: SessionEnd hook posts raw transcript JSONL; operator runs Haiku 4.5 to produce a structured session summary and writes one episodic event, idempotent on session_id. **Procedural memory tier shipped 2026-05-03**: SKILL.md-shaped events under `/memories/procedural/<name>` render as a Skills section in the prelude with surface-aware filtering via optional `applies_to: [...]` frontmatter. OpenAI, Google, and Cursor adapters slot in alongside as those tools ship. Decoupling the internal API from any single vendor is what makes Coexo memory survive model and platform changes.

Always-loaded prelude generator

Produces the small set of pointers, identity facts, and skill descriptions that ship in every session's system prompt. Live on Claude Code on dymontlabs since 2026-05-01: operator exposes `GET /prelude` (bearer-required), which renders four sections — **About Stephan / Fleet / Working preferences** (`mirix_type='core'` events grouped by `body.section`), **Skills** (`mirix_type='procedural'` events from the `memory_paths` projection joined to the live event), and **Recent focus** (top-N recent high-importance non-core, non-procedural pointer events). Surface identity is bound to the bearer token, not client-supplied. SessionStart hook makes a direct HTTP call to the operator on its tailnet IP with the bearer header, injects the response via `hookSpecificOutput.additionalContext`, and fails open on operator unavailability — ~70 ms end-to-end. Every prelude read writes an `audit_log` entry recording surface, token estimate, and the event ids that shaped it. **Surface-aware skill filtering shipped 2026-05-03** — the prelude builder respects optional `applies_to: [surface_a, ...]` frontmatter so per-host skills (e.g. `deploy-coexo-site`) only render on the surfaces that can act on them; omitted = renders everywhere. Per-source bearer auth + source-binding makes adding a second surface mechanical (generate token, add SHA256 hash to the operator's token file, install the hook on the new host). Cross-surface activation is the v1 success-signal anchor for the prelude.

Consolidation engine

Background workers that keep memory tidy across four cadences — live (rule-based admission), light (continuous extraction), REM (weekly dedup), deep (daily forgetting). Per-conversation: inline append-only with five-factor A-MAC admission scoring (mostly rules; LLM only for borderline events). Three-phase async consolidation worker reshaped 2026-05-02 after deep research into OpenClaw memory-core's well-engineered design and the broader 2024–26 sleep-replay literature. **All three phases shipped 2026-05-03.** **Light phase**: Haiku 4.5 entity/triple extraction with forced tool_choice on a structured `record_extraction` schema (slugged entity IDs, restricted entity types, XOR object_id|object_literal); upserts entities, inserts triples one row per assertion with `source_event_id` provenance back to the originating event. Append-only `consolidation_log` table tracks per-event/per-phase runs so reprocessing is idempotent and the events table stays UPDATE-free (per the AGENTS.md convention). **REM phase (weekly)**: A-MEM-style entity deduplication — cheap heuristic candidate generation (display_name match within entity_type, slug Levenshtein ≥ 0.7) feeds Haiku 4.5 with forced tool_choice on a `merge_decision` schema; execution re-points triples on subject and object, tombstones self-looped triples, unions aliases/attributes onto the canonical, accumulates recall_count, tombstones the loser. First-pass run merged 5 genuine duplicates the light phase had produced; idempotent re-runs catch each new duplicate the next pass (most recently `concept:hippo-rag-2` ← `concept:hipporag-2`). The append-only `recall_log` sibling table (migration `0005_recall_log`) records every `/recall` hit so Hebbian signal can accumulate as a queryable column without ever UPDATE-ing events. **Deep phase (daily, no-LLM first cut)**: tombstone-only **active forgetting** (events with `importance < 0.2`, zero recall_log hits in 30d, age > 30d, mirix_type ≠ 'core', not sensitive, and not referenced as `provenance.derived_from` by any non-tombstoned successor — preserves write-back hook chains and memory-tool str_replace/insert chains; capped at 100 per pass; hard-delete intentionally NOT in v1, waits for actual disk pressure to justify giving up reversibility) plus **HNSW drift check** (REINDEX CONCURRENTLY when n_dead_tup/total > 20% AND total > 100; PG17+ MAINTAIN privilege grants the operator role REINDEX without table ownership). **Deferred within these phases until substrate matures**: Generative-Agents-style threshold reflection (cluster recent episodic events into semantic claims, Park 2023) and Hebbian episodic→semantic promotion via 6-component scorer (frequency × relevance × diversity × recency × consolidation × concept-tags, inspired by OpenClaw memory-core) — both need hundreds of events and weeks of recall traffic in `recall_log` to tune meaningfully; Sonnet contradiction gate for NLI promotion (lands with the promotion pipeline); A-MEM-style memory evolution that re-writes related events' contextual prefixes (arxiv 2502.12110); LightMem-style parallel per-key consolidation queue (arxiv 2510.18866). Monthly full reconciliation that re-derives projections from raw events sits behind the same maturity threshold. Destructive operations stay off the live write path — live turns only append.

Mutation audit log

Every change to memory is appended to a tamper-evident chain that can be verified in code. Hash-chained `audit_log` table on coexo-memory, INSERT-only at the trigger level (UPDATE and DELETE blocked). Every operator write produces an audit entry chained to the previous row's hash, with UTC-normalised timestamps. The operator's `/audit/verify` endpoint walks the chain and returns ok plus the first broken id (currently null). v0 covers operator-initiated writes; v1 extends coverage to projection regenerations with old/new value capture so every regeneration is reversible.

Multi-modal ingestion pipelines

Modality-specific embedders — text, image, audio — joined at the graph layer. v0 ships the Telegram text adapter as the first source. Audio transcribed-then-embedded by default; raw artefacts persisted on disk with content-hash keys. Plaud, WhatsApp, Gmail, and image embeddings land in v1; one source channel proves the pattern.

Knowledge Vault

Sensitive content — credentials, sensitive contact data, intimate personality inferences — stored without vector embeddings and behind explicit access controls. Never enters open retrieval; addressed by ID only. v0 ships the `is_sensitive` flag on events (the embedding worker skips sensitive rows) plus pgcrypto loaded at the cluster level; per-section column encryption and the Vault ACL surface land in v1.

Importance scoring

Each new event gets a calibrated score so the substrate knows what to keep prominent and what to age out. Five-factor admission scoring per the A-MAC pattern (March 2026): utility, confidence, novelty, recency, and type prior — combined into a single calibrated score. The MIRIX cognitive type tag is the dominant factor (distinguishing persistent preferences from transient frustration matters more than any single content signal). Four of five factors compute in tens of milliseconds via SQL and embedding lookups; only the utility factor requires an LLM call, and only on borderline events. Result: roughly 70% reduction in admission LLM calls without quality loss, fully interpretable scoring. v0 ships an A-MAC band assignment with a 0.5 utility placeholder; the LLM-backed utility scorer for borderline events lands in v0.5.

Embedding model registry and re-embedding job

Tracks model and version per embedded chunk. v0 ships the registry columns (`embedding_model`, `embedding_version`) on `event_embeddings`; the selective re-embedding job that re-runs only the chunks for a given model version lands in v0.5.

Operational infrastructure

Backups, monitoring, security, and disaster recovery — layered so any single failure is recoverable. Backups via pgBackRest (Percona's distribution, after the upstream archival on 2026-04-27) to two repositories — local NVMe and Hetzner Storage Box over SFTP — both client-side encrypted with distinct passphrases held in 1Password. RPO 5 minutes, RTO 1 hour. Monthly automated restore drill, quarterly full disaster drill from cold metal using only the runbook plus 1Password. Observability runs off-host so monitoring survives any memory-host outage: Prometheus + Grafana + Loki run on dymontlabs (Stephan's existing ops box, fleet-wide monitoring), and Langfuse Cloud (EU region) collects LLM traces via OpenTelemetry GenAI semantic conventions. Vector-specific metrics tracked: dead-tuple ratio, HNSW cache hit ratio, monthly recall measurement against a fixed eval set. Substrate integrity defended in software in lieu of ECC RAM (end-of-life as a managed-dedicated product on consumer Ryzen): Postgres `data_checksums=on`, raw events as canonical truth, monthly full reconciliation that re-derives projections from raw events, plus the encrypted off-host backup. Security is layered: Tailscale ACL tags for service identity, Tailscale Serve identity headers for request-time auth, per-agent scoped bearer tokens for blast-radius control. Postgres role separation across owner / operator / readonly / audit (INSERT-only) / exporter. Hash-chained audit log table with weekly external GitHub-gist anchor for tamper-evident application audit, complementing pgAudit at the database level. Secrets in 1Password as canonical, materialized at deploy. High availability and DR replication both deferred to v2 — full HA with automated failover only when warranted by a real second user or hard real-time dependence; async streaming replication to a second Hetzner DC layers in at the same point.

Notes & Future Ideas

Section-level sensitivity on person profiles — Personality and Psychology, Working Notes, and Communication Profile sections are Vault-only and never embedded into the open vector index. Identity, Professional Context, and Network can be embedded. ACL is per-section, not per-person.

Embedding model versioning — every embedded chunk records the model and version it was produced with, so the system can re-embed selectively when the model changes rather than reprocessing the whole corpus.

Cross-building-block role — Memory underlies Signal Enrichment (context retrieval), Case Lifecycle (event log + state projection), People Intelligence (profiles as projections), Direct Assistance (preference recall), and Self-Improvement (institutional memory). It is not a peer block; it is the substrate the others read and write.

Site as canonical schema reference — once v1 lands, the site documents the event types, entity tables, MIRIX type tags, and tool API as the canonical reference. Stephan and any agent reading the site know exactly what shape memory has.