Twelve layers, one system of record

A vector table stores text that looks similar to your question. That is one useful trick, and it is not memory. Remembering a business means answering twelve different questions — and keeping twelve different kinds of answer, in one database, under one tenancy boundary.

  • 12 live layers
  • One Postgres database
  • Semantic and episodic share one table
  • Nine operations on /v1/memory

Each layer earns its place by answering something the others cannot

These are not twelve tables of the same shape with different names. Each one exists because a question kept coming back that the previous layer answered badly.

LayerStored inThe question it answers
Semantic memories · mem_type=semantic What is durably true about this person, client or project?
Episodic memories · mem_type=episodic What was actually said, by whom, and when?
Bitemporal facts facts What is true now — and what did it replace?
Fabric fabric_entries What was decided, who owes what, and on what evidence?
Sessions sessions · session_messages What happened across a whole imported transcript?
Knowledge base kb_sources · kb_documents Which document says so, and is that version still current?
Graph graph_facade.py Which records connect to which entities, and how?
Entity pcnaid_entities Who and what does this business actually deal with?
Observation pcnaid_observations Which patterns keep recurring?
Procedural pcnaid_procedures How does this get done here?
Outcome pcnaid_outcomes What worked, what failed, and what follows from that?
Multimodal media_memory.py What was in that voice note, screenshot caption or scan?

A thirteenth table, context_index, exists in the tree with a repository class and no call sites — nothing reads it and nothing writes it. We count twelve, because twelve is what runs.

Two kinds of remembering, one table

Semantic and episodic memories live in the same memories table and differ by mem_type. A semantic record holds { text } — a durable statement that should still be true next month. An episodic record holds { user, assistant } — a turn that happened, kept with its timestamp so it can be quoted rather than paraphrased.

Both carry a VECTOR embedding and a search_vector generated by Postgres from the record's text, so the same row is reachable by meaning and by exact wording without a second store to keep in sync.

Episodic memory decays. A maintenance pass scores each unpinned episode with R = exp(−k·t / S) — where t is hours since it was last read and S is its strength — and archives it once R falls below 0.2. The default decay constant is 0.05, and nothing younger than 72 hours is considered. Pinned records are skipped outright.

Before that happens, consolidation can fold a cluster of three to eight related episodes, aged between 12 hours and a week, into a single semantic memory and archive the sources. The detail survives in a form worth keeping; the transcript noise does not.

pinned is_archived strength last_accessed confidence source_ids

memories

A semantic record as stored

id
9f1c4e2a-…-77e1
mem_type
semantic
content
{ "text": "Ardal & Finch invoice on net-45, not net-30." }
confidence
0.91
source_ids
fe_18b3
strength
1.0
pinned
true
created_at
2026-04-09T14:22:11Z

POST /v1/memory → embedded, indexed, audited

What you can do to a memory

Nine operations across six paths. Everything else in the product reads what these write.

OperationWhat it does
GET /v1/memory List by type, with an optional text filter and an include_archived switch.
POST /v1/memory Write a semantic memory. Accepts source_ids, confidence and arbitrary meta.
GET /v1/memory/{id} Read one record. Stored pseudonyms are resolved back through the tenant’s PII vault.
PATCH /v1/memory/{id} Edit semantic text — re-embedded on write — or set pinned and archived.
DELETE /v1/memory/{id} Delete it. When graph memory is enabled, the edges it produced go with it.
POST /v1/memory/bulk delete, pin or unpin up to 1,000 ids in one call.
POST /v1/memory/{id}/pin Exempt a record from decay and archiving, permanently.
POST /v1/memory/{id}/unpin Return it to the normal retention curve.
POST /v1/memory/{id}/force-fact-extract Queue fact extraction for this record now instead of waiting for the night run.

Writes that survive a retry

Agents retry. A dropped connection during a write must not leave you with the same memory twice, so POST /v1/memory honours an Idempotency-Key header: the first call is stored against the key, and a repeat with the same key returns the original response body rather than writing again.

Every write is checked against the tenant's monthly memory quota before it lands. Exceeding it returns 402 with the metric and the limit — a hard stop you can see coming in the dashboard, not a silent overage on next month's invoice.

Creates, edits and archives are appended to a hash-chained audit log with the operation, the memory id and the trigger that caused it. If a record changed, the chain says when and why.

Tenancy and the audit chain

You do not query the layers one at a time

Twelve layers would be twelve integrations if you had to read them yourself. You do not.

POST /v1/context takes a question, works out which layers it needs, searches them in parallel, fuses the results and returns one packed block sized to a token budget — with a manifest naming every record that made it in. The layer APIs on this page exist for when you want to write or inspect a specific layer directly.

How the retrieval engine assembles a context pack

Give your agents a memory you can audit

Run the whole system on your own hardware under the MIT licence, or ask us about hosted access. Both start from the same place.