Four actors, one git repository. Source plugins bring raw evidence in, an agent curates and proposes over MCP, you accept or reject or re-instruct — and main is a valid, typed tree at every accepted step. The agent proposes; only you accept; nothing invalid is ever committed.
Pinned, sandbox-built taps — Graph, files, git, Salesforce, another KB — fetch raw provider material and append it, unaltered, to the append-only evidence ledger.
Over MCP, reads the schema + evidence and stages record and schema edits onto a proposal ref. It can propose; there is deliberately no accept tool.
Every change is a commit. main is always valid; work happens on proposal refs; history is the audit log.
Owns the seams: clones and sandbox-builds taps, exposes the MCP surface, validates the whole tree on accept, and serves the review UI. It is the only writer to main.
The only actor who accepts. Review the diff, then accept, reject, or re-instruct the agent. Acceptance is the moment a proposal becomes truth on main.
A KB is just a git repo with this shape. It's portable, greppable, and diffable — no database, no opaque store. The schema is a real Rust crate that travels with the repo, so no domain is ever baked into the engine.
The records themselves — typed RON files, each conforming to its kind and linking to others. This is the memory.
A Rust crate — the KB's kinds, fields, enums, links, saved queries, and its validation function. Rust is the schema language.
registry.ron is the checked projection of the schema the engine + UI read; sources.ron declares evidence sources and pins each tap.
The provenance trail: which evidence, which proposal, whose acceptance produced each change. The audit log, written in-tree on accept.
The raw evidence ledger — original provider bytes, content-hashed and immutable — is append-only and local, and lives outside the git ontology repo. Only curated facts ever land in the tree.
Each pass moves evidence one controlled hop closer to trusted memory. The base is never in a half-valid state — a proposal is either accepted whole (and validated) or it isn't there at all.
A tap fetches original bytes — the email, the transcript, the SOQL rows — and appends them to the evidence ledger. Content-hashed and immutable; interpretation happens later.
Reading schema and evidence over MCP, the agent drafts record edits and any schema changes they require, staging them together on a fresh proposal ref with a summary and source links.
In the review UI you see each record's before → after, its source, and any schema impact. Accept the proposal, reject changes you don't want, or send the agent back with a note — the loop re-runs from step 2.
On accept, the proposal's own schema is compiled and run over the entire resulting tree before the merge. Pass, and main advances with a receipt; fail, and nothing moves. Pull is fail-closed too.
The invariant: main is a valid, typed tree after every accepted step — and only after acceptance. Re-instruction loops back to step 2 without ever leaving the base inconsistent.
The agent connects to kyyn over MCP. The per-kind CRUD tools are generated from the registry, so adding a kind to the schema grows the tool surface with zero engine changes. Every write stages into a proposal — never main.
Generated per kind. _set takes a complete RON record and stages it; schema_set stages the crate, registry, or sources.
proposal_validate is a dry-run of the accept gate. There is deliberately no accept tool — only you accept.
brief is the read-this-first map of the KB. Your re-instruct notes come back to the agent as pending instructions to resolve.
Sandboxed builds. Both the schema crate and every tap are compiled inside a bubblewrap jail — read-only sources, private /proc and /dev, network denied. Enforced host-allowlisting of plugin fetches at run time is on the roadmap.