Show HN: Keel – One event-sourced context graph for all your agents Angel Kurten released Keel 0.27.1, an alpha-stage event-sourced context layer that gives every AI agent in a product a shared bitemporal context graph per tenant, accessed through five MCP tools. Keel ingests structured domain events from Postgres, webhooks, Kafka and SQS rather than documents, and declares each vertical in a YAML "cartridge" so the core stays domain-agnostic; Kurten argues existing memory layers such as Zep, Mem0, AgentCore Memory and Neo4j's MKG model memory per user or per agent and do not start from domain events or model the tenant. The container ships at ghcr.io/angelkurten/keel with an HTTP JSON API, semantic search, an agent registry and SDKs for TypeScript, Python, Go and PHP, running on an in-memory store, SQLite or MySQL. Stop rebuilding the pipeline for every agent. Docs: angelkurten.github.io/keel https://angelkurten.github.io/keel/ · Why: We built four agents and four copies of our customer context https://angelkurten.com/blog/keel-four-agents-four-copies-of-context Keel is a context layer for the AI agents inside a product: a vertical SaaS, a consumer app, an internal tool. Domain events from your product go in; a bitemporal context graph per tenant comes out, whatever your unit is an end-customer, a household, a team ; every agent you build reads and writes it through the same five MCP tools. You describe your vertical once, in a cartridge ontology, event catalog, semantic rules, memory policies . Keel's core knows nothing about your domain. Your second agent connects in a day instead of weeks, because there is no second pipeline to write. Status: 0.27.1, alpha. Container: ghcr.io/angelkurten/keel . The five tools over MCP and an HTTP JSON API /v1 , Postgres, webhook, Kafka and SQS adapters, semantic search, consolidation, an agent registry and SDKs for TypeScript, Python, Go and PHP, on the in-memory store, SQLite --db keel.db , stdlib, no server to run or MySQL --db mysql://... . Every new agent in a product repeats the same work: extract the customer's data, normalize it, decide what "critical" means, keep its own memory, expose its own tools to the model. Four agents, four copies of the customer's context, four definitions of "critical", four memories that never talk. Existing memory layers Zep, Mem0, AgentCore Memory, Neo4j's MKG ingest text and documents and model memory per user or per agent. None of them start from domain events , and none model the tenant your product already has: the end-customer of a B2B SaaS, the household of a consumer app, the team of an internal tool. 1. Cartridges. A vertical is declared in YAML plus a few ten-line handlers. The core stays generic. Write a cartridge for fleets, clinics, logistics, or your own product. 2. Events in, not documents. Keel ingests structured facts with time Postgres tables, webhooks, Kafka, SQS . Ingestion is deterministic and cheap. The graph is exact because it is born from events, not inferred from text. 3. Every fact carries its provenance. Source, valid from , valid to , recorded at , and who corrected it. Audit-ready by construction. 4. One graph per tenant. One subgraph per end-customer, household or team, with its own permissions and expiry. context subject, as of?, depth?, as known at?, task?, detail?, exclude?, only? - what is true now, at as of, or as known at a past moment; rules and their verdicts; depth=1 adds the neighbors; detail="concise" for about a third of the tokens; only= ... just those predicates history subject?, since?, until?, predicate?, scope?, as known at?, task?, changed since?, limit? - what was true and when we learned it; without a subject, what changed since a watermark search text?, kind?, rule?, limit?, after?, mode?, min score?, related to?, via?, detail?, depth?, per kind?, exclude?, as of?, where? - text or semantic search, or every entity of a kind / where a rule fires, paged; related to and via keeps only one subject's neighbors; detail="concise" puts each result's facts in it; per kind=n the best of every kind: the context for a task in one call where={name: value} by an attribute's or a fact's value; each result says what matched matched, snippet ; kind may be a list of kinds annotate subject, predicate, value, scope?, evidence?, by?, task?, key?, status? - an agent or a person writes what it learned; key makes retries safe; status="proposed" waits for review related subject, relation?, as of?, depth? - neighbors by relation type, up to three hops search mode="semantic" finds subjects by meaning: ids in the query first, then a dense and a lexical leg fused, then an optional reranker. Where vectors live and which model makes them are adapters you choose: in memory, Chroma, Weaviate, any OpenAI-compatible embeddings endpoint, or your own vector database and model how https://github.com/angelkurten/keel/blob/master/docs/connect.md semantic-search , example https://github.com/angelkurten/keel/blob/master/examples/custom adapters.py . Expose them over MCP keel serve and point any agent framework at them. Declare agents: in the cartridge and keel serve --agent