§ /architecture — 15 tables · 1 function · one postgres

One container.
One database.
Two pipelines.

System design, schema, API surface, and the decisions behind each constraint. Runs locally against any Postgres 14+.

read · 10 mindb · PostgreSQL 14+ports · 8080/mcp · 8080/api
§ 01 — TOPOLOGY

Runtime, in three boxes.

A single borg-engine process talks MCP and REST on :8080. It reads from and writes to one Postgres, and calls out to OpenAI or Azure OpenAI only for extraction and embeddings.

:8080engine

borg-engine

  • FastAPI + FastMCP 3
  • MCP endpoint /mcp
  • REST /api/{think,learn,recall}
  • Background extraction worker
  • 24 h snapshot loop
source of truth

PostgreSQL 14+

  • pgvector, pgAudit, uuid-ossp
  • borg_* tables — 15 + 1 fn
  • Managed · self-hosted · local
  • Recursive CTE traversal
extraction

OpenAI · Azure OpenAI

  • text-embedding-3-small — 1 536-dim
  • gpt-5-mini / gpt-4o-mini
  • BYO key. Off-loaded async — never blocks borg_think
§ 02 — SCHEMA

Fifteen tables.
One function.

Every table cleanly separates canonical data from derived serving state. Hot-path denormalization lives in *_state, the source of truth in the parent table.

TableRoleTypeStatus
borg_episodesImmutable evidence layerCanonical✓ ships
borg_entitiesGraph nodes — typed, namespacedCanonical✓ ships
borg_entity_stateTier, salience, access countersDerived✓ ships
borg_factsGraph edges — temporal, with supersessionCanonical✓ ships
borg_fact_stateSalience + access for factsDerived✓ ships
borg_proceduresCandidate behavioral patternsCanonical✓ ships
borg_predicates24 canonical relationship verbsReference✓ ships
borg_predicate_candidatesNon-canonical, pending reviewQueue✓ ships
borg_resolution_conflictsAmbiguous entity matchesQueue✓ ships
borg_namespace_configPer-namespace token budgetsConfig✓ ships
borg_audit_logFull compile + extraction tracesAudit✓ ships
borg_snapshots24 h hot-tier state capturesSnapshot✓ ships

borg_traverse() — recursive CTE function

Used by the graph_neighborhood retrieval strategy. 1–2 hop traversal, cycle-safe via path tracking, scoped to a single namespace.

-- 1-2 hop graph walk, cycle-safe, ns-scoped
SELECT * FROM borg_traverse(
  p_entity_id := 'a1b2c3…',
  p_max_hops := 2,
  p_namespace := 'product-engineering'
);
-- returns: entity_id, entity_name, entity_type,
-- fact_id, predicate, evidence_status,
-- hop_depth, path
§ 03 — API SURFACE

Three MCP tools.
REST mirror. Admin.

OSS release runs locally with no authentication — single-user by design. Auth for shared deployments is on the roadmap; the schema already supports it.

core — mcp + rest
POST/mcp
POST/api/think
POST/api/learn
POST/api/recall
namespaces
GET/api/namespaces
GET/api/namespaces/:ns
POST/api/namespaces
PUT/api/namespaces/:ns
DEL/api/namespaces/:ns
admin
GET/api/admin/queue
GET/api/admin/entities
GET/api/admin/facts
GET/api/admin/procedures
GET/api/admin/conflicts
GET/api/admin/predicates
POST/admin/process-episode
POST/admin/requeue-failed
POST/admin/snapshot
§ 04 — DECISIONS

The constraints
are intentional.

Every choice below has a failure mode it prevents and a cost it accepts. If you disagree, file an issue.

Why LLM in the write path?
Borg extracts structured knowledge — not text blobs. That requires an LLM. The cost is extraction latency and tokens; the mitigation is that it runs entirely offline and never blocks borg_think. The alternative (embed-only) gives you similarity search but not a queryable graph.
Why not Neo4j or FalkorDB?
Adding a graph DB means syncing two systems. Sync means drift. PostgreSQL recursive CTEs handle 1–2 hop traversal at the expected scale (hundreds of entities, thousands of facts). A dedicated graph DB is a measured-bottleneck escape hatch, not a starting point.
Why three-pass resolution, not always-merge?
Collision is catastrophic. Fragmentation is recoverable. The 0.92 semantic threshold is deliberately high. The 0.03 ambiguity gap flags uncertain matches for human review. You can merge entities manually; you can never safely un-merge them.
Why per-task memory weights, not one ranking?
Debug needs episodic recall and procedures. Compliance needs episodic evidence and semantic facts, but should never surface unverified procedures. One ranking can't serve both — so the weights change per task, not the code.
Why two output formats?
Claude reasons over structured XML attributes. GPT prefers compact JSON. Sending XML to GPT wastes tokens on tags it ignores; sending flat JSON to Claude loses metadata Claude uses. Two formats, chosen by the model parameter.