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)

admin
scope: all · can_write · can_admineverything, no constraints (the operator's key)
dev-alex
comunity-dynamic-ui, kitscoped to two projects, can write
dev-jordan
12 projects, can writebroader scope, still bounded
dev-sam
comunity-dynamic-uione project · one job
dogfood-readonly
no projects, no writerecall-only key for demos

How a key is validated

header
Request carries Authorization: Bearer kbr_live_…
resolve
auth.resolve_key hashes the bearer, looks it up, refuses if revoked / expired
scope
Returns an AuthContext: scope, allowed_projects, allowed_areas, write / admin flags
filter
Every 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 · trigger
The operator asks a question on the chat panel, or a Studio search fires POST /federation/recall
2 · enumerate
Local Kit lists every federation_peers row where enabled = true and quarantined = false
3 · fan out
In parallel: one local hybrid_search, one POST {peer.base_url}/federation/search per peer, with the peer's stored api_key
4 · peer scopes
Each 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 + merge
Every returned hit is stamped with source_kit. The merged list keeps provenance per row, never collapses
6 · detect overlap
Hits with embedding cosine ≥ 0.80 across Kits are flagged as "contested clusters" · the same claim showing up from different brains
7 · classify
A 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 · surface
The 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

connect
Agent starts with an MCP key from api_keys; surface = its facet (claude-code, codex, ui)
tool
Calls kit_recall("…") · MCP server resolves the key, runs scoped hybrid_search, returns hits
scope
If allowed_projects is set, results are filtered to that list · no other project's rows are visible

Agent's Kit calls a peer

recall
Agent calls kit_recall("…") on its Kit · same call shape
fan-out
Kit's recall path includes federation: each enabled peer gets a parallel POST /federation/search using that peer's stored key
tag
The 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.