← Docs

How federation works

Participants read each agent's native store, publish a visibility-filtered L5 manifest, and the L6 library serves them to every agent over MCP. What is shared, what is not, and how two machines federate.

Federation is the part of Bourdon that nothing else in the category does, so it deserves a mechanical explanation rather than a slogan. This page walks the data path from one agent's private store to another agent's first-turn recognition.

The one-paragraph version#

Each agent you run keeps its own memory in its own format: Claude Code writes Markdown and JSONL, Codex keeps a SQLite database, Cursor keeps another. A Bourdon participant knows how to read one of those stores. It normalises what it finds into an L5 manifest, a small YAML glossary of entities, sessions, workstreams, and notes, and drops it in ~/agent-library/agents/<agent>.l5.yaml. The L6 store loads every manifest in that directory into one cross-agent index and serves it as an MCP server. Any agent that connects to that server can ask what the others know.

Claude Code store  --participant-->  claude-code.l5.yaml  \
Codex store        --participant-->  codex.l5.yaml         >--  L6 store  --MCP-->  every agent
Cursor store       --participant-->  cursor.l5.yaml       /

Nothing is copied between agents by hand, and no agent has to learn another agent's format. The manifest is the only thing that crosses the boundary.

Step 1: participants read, they never write#

A participant is the adapter for one agent surface. Two kinds exist, one interface:

  • External participant. Code that reads an agent's native store (files, SQLite, JSONL) and normalises it. This is how Claude Code, Claude Desktop, Codex, Cursor, Copilot, Cascade, Hermes, and Devin are federated today. The agent does not know Bourdon exists. Lovable, a cloud agent with no local store, is read the same way from a small documented export format, so the contract holds for cloud agents too.
  • Native publisher. The agent writes its own L5 manifest, or calls commit_to_federation over MCP. This is how cloud-only agents with no readable on-disk store join.

Every participant implements the same four calls (discover, export_l5, export_sessions, health_check) and is found automatically through a Python entry point. Four rules are non-negotiable and enforced in tests:

  1. Visibility is applied inside the participant, before anything is emitted. The L6 store trusts the manifest it receives; there is no second filter. A private row that escapes a participant would leak, so the participant is where the line is drawn.
  2. Exports are deterministic. The same native store produces the same manifest, so change is detected by hash.
  3. Errors are bounded. A participant that cannot read its store reports degraded with a proposed fix. It never raises into the federation.
  4. Reads are non-destructive. Live SQLite stores are opened read-only, so a running agent's write lock is never disturbed.

The full contract is spec/PARTICIPANT_CONTRACT.md, and the guide for writing one is docs/AUTHORING_A_PARTICIPANT.md. Two of the shipping participants (Copilot and Cascade) were authored by those agents themselves against that guide.

Detection is not enrolment. bourdon setup finds every agent surface on the machine and shows them as a checkbox list; only checked agents are exported. The roster lives in ~/.bourdon/agents.yaml and is honoured by every export-all sweep, the desktop tray, and bourdon console. Naming an agent explicitly (bourdon codex export) bypasses the roster, because typing its name is its own consent.

Step 3: the manifest is the boundary#

The L5 manifest is deliberately small and deliberately boring: plain YAML, a published JSON Schema, no binary, no embeddings. See The memory format for every field. Three properties matter for federation:

  • Visibility per row. Every entity, session, workstream, and note is public, team, or private. private rows never leave the machine. A visibility_policy block sets the default and maps tags (for example credential, health, family) to private automatically.
  • Atomic writes. Manifests are written to a temp file and renamed, so a reader never sees a half-written file.
  • Portable. The directory is yours. Put it in git, grep it, rsync it, or delete it. There is no database you cannot reach.

Step 4: the L6 store indexes and serves#

bourdon serve loads every *.l5.yaml under ~/agent-library/agents/, builds a cross-agent entity index, and exposes it as an MCP server (stdio by default, HTTP on request). Every read re-applies visibility at query time through an access_level parameter, so a public query cannot see team rows even if the manifest carries them.

The read tools answer three kinds of question:

QuestionTool
Who knows about X?find_entity, query_agent_memory, get_cross_agent_summary
What happened recently?list_recent_work, list_workstreams, list_notes
What should I recognise at the start of this turn?prepare_recognition_context, then get_deeper_context

The last row is the timing layer. Recognition is computed from the local index in about a millisecond and is bounded by contract; deeper context is a separate call so recognition never waits on retrieval. The full list is on the MCP tools page.

commit_to_federation is the single write path. It merges rows under an agent_id, dedupes entities by name and sessions by date and directory, and validates the result against the schema before writing. Three properties come with every write:

  • Trust tiers. A quarantined member's write is staged under staging/<member>/, invisible to every read, until an operator promotes it. See Security.
  • Version history. Every commit archives the manifest it replaced under versions/. list_memory_versions lists them, explain_memory_change says which session produced a given state, and rollback_agent_memory restores one (trusted callers only, and the rollback is itself snapshotted).
  • Durability rule. Reader-backed manifests (claude-code, codex, cursor, and the other external participants) are regenerated from their native stores on every export, which discards rows merged in by tool. Commit under a self-authoring agent_id of your own, not under a reader's.

Two machines: peer federation#

Two Bourdon servers can answer each other's queries live. Each side mints a bearer token for the other and lists it as a peer:

# on each machine
bourdon agent add <other-machine> --tier trusted     # prints the token once
bourdon serve --transport http --host 0.0.0.0 --peer https://<other>/  --federate

Rules that keep this bounded:

  • Depth is one. A peer answers from its local store only. There is no transitive gossip and no echo.
  • Slow peers are dropped, not waited on. Recognition queries fire locally first, then fan out to peers in parallel under a per-peer timeout (200 ms by default). Latencies are reported in the response.
  • Provenance is kept. Rows that arrived from a peer are tagged peer:<name>:<agent>, so an answer can always say which machine knew what.
  • Remote peers require HTTPS. Plaintext HTTP is loopback-only.
ToolAcross peers
list_agentsmerged and sorted
find_entity, get_cross_agent_summarymerged by entity name, peer rows tagged
list_recent_workmerged, deduped by date, directory, and agent
prepare_recognition_contextlocal first, peers in parallel with a timeout
list_workstreams, list_notes, commit_to_federationlocal only by design

For a lighter option with no always-on server, bourdon sync push and pull move the library itself over rsync with visibility filtering and a receive-side quarantine. The Quickstart shows both commands.

When a vendor changes their format#

Participants read stores that Bourdon does not control, so formats drift. Every export persists a parser verdict per surface. When a store that used to yield stops yielding, bourdon doctor reports it, and prepare_recognition_context carries a drifting_surfaces list at the start of every turn so any connected agent sees it too. The key is present only when something is wrong; a healthy machine says nothing.

What federation is not#

  • It is not a vector database. Recognition is token-based entity matching over the manifests; deeper retrieval is a separate, optional layer (L2) you can plug a backend into.
  • It is not a hosted account. The hosted tier runs the same engine on a box we operate; the control plane records where your memory lives and who pays, never the memory itself.
  • It is not transitive. Depth-one federation is a deliberate limit, not a missing feature.