Capsule Architecture

A capsule is Rohrpost’s unit of raw knowledge — a prompt, a response, a diff, an error, or a note, exactly as it happened. Capsules are the immutable source of truth; everything else (chunks, embeddings, graph entities, summaries) is derived from them by the distillation pipeline, and can be rebuilt from the capsule if a downstream index ever needs to be recomputed.

What a capsule holds

Each capsule stores:

Field Purpose
Source Where it came from — an MCP client, the CLI, the OpenAI-compatible proxy, an IDE plugin
Kind prompt, response, tool_call, diff, error, or note
Content The raw text
Metadata Arbitrary tags — project, session ID, affect, and other structured fields used by ranking and filtering
Created at Timestamp, used for recency scoring and decay

Capsules are written once to Postgres and archived as immutable JSON in Garage (S3-compatible object storage) for audit and replay.

The distillation pipeline

Every capsule flows through the same pipeline, asynchronously, via NATS JetStream:

  1. Capture — the gateway authenticates the request, attaches the tenant, and publishes the capsule to NATS the moment it arrives
  2. Chunk — a distiller worker splits the content into semantically coherent chunks
  3. Embed — each chunk gets a vector embedding (via a local Ollama model — nomic-embed-text by default) and is indexed for both vector similarity search (pgvector) and BM25 keyword search (Tantivy)
  4. Extract — entities and relationships mentioned in the chunk (files, functions, decisions, people, tools) are extracted and written into the knowledge graph (Apache AGE)
  5. Summarize (periodic, not per-capsule) — once a session or feature scope accumulates enough capsules, a hierarchical summary is generated and stored separately for fast high-level recall

None of this blocks the original request — NATS JetStream guarantees the capsule is durably queued even if a distiller worker is temporarily down, so nothing captured is ever lost to a crash mid-pipeline.

Recall

A query runs vector search, BM25, and (when the query mentions or resolves to a known entity) one-hop graph expansion in parallel, merges the results with Reciprocal Rank Fusion, optionally reranks with a cross-encoder if one is configured, and packs the top hits into a markdown brief sized to your token budget. The brief only ever includes what’s relevant to the query — not the full accumulated history — which is what keeps token usage bounded as your knowledge base grows.

Multi-tenant isolation

Capsules are scoped to a tenant, and optionally a project within that tenant. Isolation is enforced at the storage layer, not just in application logic: each tenant gets its own Postgres schema, so a query in one tenant’s scope cannot surface another tenant’s capsules even under a bug in a shared query path.