How Kits federate, and how knowledge crosses between them
There isn't one Kit; there are many. The operator's Kit, a Kit running on the company VPN, a developer's scoped Kit on their laptop. They share knowledge through scoped, key-gated calls, never by merging brains. This is how that works.
drawn from the live code · services/federation.py · routes/federation.py · services/auth.py · models.ApiKey · models.FederationPeer
a Kit instance (substrate + brain + agents)
admin key · full read/write
developer key · scoped, write-allowed
peer key · scoped read-only across Kits
conflict cluster · same claim from different Kits
1
One Kit, many Kits · the shape of federation
A Kit is one Postgres database, one FastAPI process, one set of agents, and one operator (usually). Federation lets one Kit ask another for its memories in a structured, key-scoped way. No merging, no shared schema, no hidden writes.
kit-id: kit · operator: the principal
Operator's Kit
The personal Kit. Holds the soul, the relationship, deep research, primary project work. Federates outward to scoped peers when a search benefits from cross-Kit knowledge.
admin key all projects, write, admin
scoped API key a subset of projects
peer key → kit-corp federated read
kit-id: kit-corp · operator: a team
A company's Kit
A peer Kit running on the same machine or a different one. Has its own brain, its own agents, its own operator(s). Reachable over its own base_url. Returns scoped results to a calling peer, never raw rows.
peer api key what kit-corp lets others see
team dev keys what its operators see
federation_peers.api_key · scoped on receive
kit-id: kit-dev · operator: a developer
A developer's Kit
An individual developer's Kit. Receives a project-scoped key from the host Kit, can read only what that key allows. Can also be a peer that the host Kit reaches into for the developer's notes.
dev-jordan simvida, kit, loom, …
dev-sam comunity-dynamic-ui only
Why federation rather than one shared brain: a shared brain would have to negotiate identity, consent, and conflict at every write. Federation does the opposite: each Kit owns its own writes, and reads cross the boundary only when a key allows it. The cost is duplicate claims; the gain is honest provenance and clean revocation.
↓
2
The API key · what gates everything
Nothing crosses a Kit boundary without a key. Keys are made on the host Kit, given to a caller, and validated on every request. The host decides what the key can see; the caller can never widen their own access.
Identity
A short label (e.g. dev-alex), a hashed token starting kbr_live_, and a kind: api, mcp, peer, or admin.
api_keys · key_hash + kind
Scope · projects
An array of project slugs the key may read. Empty + scope=all means everything; otherwise the recall query is filtered server-side.
allowed_projects · varchar[]
Scope · knowledge areas
A second axis: knowledge_areas the key can touch. Areas are coarse-grained topics (a future taxonomy), enforced the same way as projects.
allowed_areas · text[]
Capabilities
can_write decides whether POST /memories succeeds; can_admin unlocks the key-management endpoints. Most keys are read-only.
can_write · can_admin
Real keys, real scopes (in this Kit today)
adminscope: all · can_write · can_admineverything, no constraints (the operator's key)
dev-alexcomunity-dynamic-ui, kitscoped to two projects, can write
dev-jordan12 projects, can writebroader scope, still bounded
dev-samcomunity-dynamic-uione project · one job
dogfood-readonlyno projects, no writerecall-only key for demos
How a key is validated
headerRequest carries Authorization: Bearer kbr_live_…
resolveauth.resolve_key hashes the bearer, looks it up, refuses if revoked / expired
scopeReturns an AuthContext: scope, allowed_projects, allowed_areas, write / admin flags
filterEvery recall / list / read call applies the scope as a WHERE clause. No code path can opt out.
Two surfaces use these keys: MCP (an agent calls Kit via the Model Context Protocol, the same agent-to-Kit channel Forest and Taz use), and HTTP (the work-item triggers, the chat panel, federation peers). Both share the same api_keys table; only the surface label differs.
↓
3
The federated call · how a search crosses Kits
A single recall fans out: the local hybrid search runs in parallel with one POST to each enabled peer. Every hit comes back tagged with the Kit it came from. The merge keeps provenance honest.
1 · triggerThe operator asks a question on the chat panel, or a Studio search fires POST /federation/recall
2 · enumerateLocal Kit lists every federation_peers row where enabled = true and quarantined = false
3 · fan outIn parallel: one local hybrid_search, one POST {peer.base_url}/federation/search per peer, with the peer's stored api_key
4 · peer scopesEach peer validates the key, runs its own scoped hybrid_search, returns hits. A peer being slow or down never breaks the caller; bad peers degrade gracefully and get logged.
5 · tag + mergeEvery returned hit is stamped with source_kit. The merged list keeps provenance per row, never collapses
6 · detect overlapHits with embedding cosine ≥ 0.80 across Kits are flagged as "contested clusters" · the same claim showing up from different brains
7 · classifyA small LLM call labels each contested cluster as agree, disagree, or related with a one-line note. Only fires on the small overlapping set, never on the whole result
8 · surfaceThe UI receives the merged list plus the contested clusters; each hit colour-coded by source_kit, conflicts visible inline
What the peer can and can't see: the caller sends a query embedding and the structured filters; the peer never sees the full prompt, never sees the rest of the calling Kit's substrate, never sees the operator's identity beyond what is encoded in the key. The returning hits are the same shape any local recall produces, minus rows that fall outside the peer key's scope.
↓
4
What crosses the wire · and what stays put
Federation is read-only and explicit. Writes never cross silently. The only cross-Kit write primitive is federation_edges, an explicit annotation one Kit makes about another.
Crosses the wire
Search hits (id, title, content, embeddings, tags, project, scope, tier, created_at). Each tagged with source_kit. Subject to peer key scope.
/federation/search · /federation/recall
On request
A single memory by id (when the operator clicks through to a peer's row). Returns the memory's full content. Same scope check.
/federation/memory/{id}
Explicit annotation
One Kit can write a typed edge into the other (corroborates, contradicts, supersedes). Recorded with from_kit, to_kit, written_by, optional note. Revocable.
federation_edges · /federation/edges
Never crosses
Soul memories, conversation transcripts, agent dispatch streams, the operator's private chat history, embeddings of out-of-scope projects, any row outside the peer key's scope.
enforced server-side by AuthContext
The trust loop: a peer that misbehaves (returns junk, times out repeatedly, sends conflicting claims that don't hold up) can be quarantined with one field flip. Quarantine doesn't delete history, it just stops fan-out. Heavy machinery from #2747 (signed manifests, ledger, postures) is deferred until the simple version proves it's needed.
↓
5
Using it · how an agent reaches another Kit's knowledge
Most of the time, an agent only needs its own Kit. But when it does need a peer (a developer reading the company Kit's playbook, the host Kit pulling a design note from the dev's local Kit), the path is consistent.
Agent calls its own Kit
connectAgent starts with an MCP key from api_keys; surface = its facet (claude-code, codex, ui)
toolCalls kit_recall("…") · MCP server resolves the key, runs scoped hybrid_search, returns hits
scopeIf allowed_projects is set, results are filtered to that list · no other project's rows are visible
Agent's Kit calls a peer
recallAgent calls kit_recall("…") on its Kit · same call shape
fan-outKit's recall path includes federation: each enabled peer gets a parallel POST /federation/search using that peer's stored key
tagThe agent sees one merged list, but every cross-Kit hit carries source_kit: "kit-corp" · the agent (and the operator) always know which brain a fact came from
The developer onboarding flow: each developer (Alex, Sam, Jordan) has their own API key with project scope; calling kit_recall with that key returns only what the key allows; switching keys in the panel demonstrates the same Kit substrate appearing differently per developer.
The shape that makes it scale:
a key never widens (only the host Kit can change scope) · a peer is one HTTP endpoint away (no shared schema, no migrations) · provenance survives the merge (source_kit on every row) · conflicts are surfaced, not resolved (the operator decides) · revocation is one field flip (quarantine = stop) · the heavy trust machinery is deferred until the simple version cracks.