Chio/Docs
LOGIN · JOIN

Federation & Swarms

Let another operator’s agents work under your authority while each kernel enforces its own rules and both sign the result.

Concept chio@5f63e6e

On this page

Two organizations can share agent work without either one trusting the other’s machines. Everything that crosses the boundary is signed: a grant that only narrows, a treaty each kernel computes for itself, and a receipt both kernels sign. Each operator keeps its keys, its policy, and the last check on its own side.

Chio splits the problem in two. Federation establishes the relationship: which peers an operator recognizes and which shared rules govern their cooperation. Swarm authority binds the work: who may delegate to whom, how far authority may descend, and which budget, route, and revocation state constrain each child task. Recognizing a peer and trusting a capability issuer are separately configured decisions.

Authority crosses the boundary; enforcement stays local

An operator can coordinate a parent task with a partner’s worker without granting that worker the parent’s entire authority. The parent holds a signed grant. The child task receives authority derived from it and narrower than it. The receiving kernel admits the child’s calls under its own trust rules before its local tool runs.

Each operator runs its own parts. Operator A holds the parent grant, runs a revocation oracle that commits its revocations to signed epoch roots, and keeps the receipts it may later share. Operator B runs the worker the child names and the kernel that admits the worker’s calls. Three things cross between them: the delegated capability, the transport that carries peering and revocation roots, and an evidence package.

Operator peeringInspect what crosses.
Operator AOperator BOperator AOperator B

Every crossing ends at a check in operator B

Operator A delegates, publishes revocation roots, and exports evidence; operator B checks each crossing before its kernel admits the call. Each operator runs the same parts, and the roles reverse when B delegates to A. Federation and swarms.

delegated capability
B’s kernel verifies the parent against its own trusted issuers and the child against the parent subject, inside the treaty’s intersection.
transport
B admits a connection only from an authenticated peer whose key it pinned; every lane keeps its own verifier above the transport.
evidence package
B verifies the package offline against signer keys it chooses, and records the share on import.
One direction of a symmetric relationship: each operator runs the same parts, and the roles reverse when B delegates to A. Treaty terms, signals, and co-signing are left out.

Every crossing ends at a check on the receiving side, and none of them installs trust by itself. Pinning A as a federation peer does not add A’s key to B’s trusted capability issuers; that trust is configured separately. Revocation roots merge all or nothing: a batch with one root that does not verify against the oracle key pinned in B’s directory merges nothing into B’s revocation sink.

Three signed artifacts carry the bounds. The task graph bounds recursion and fan-out. Delegation witnesses establish that each step narrowed scope. A continuation binds the child to its route, its allocation, and the revocation epoch it was issued under. The receiving kernel checks them in a fixed order:

CheckWhat it establishes
Witness issuers are configuredThe host supplies the trusted witness issuer keys. Keys inside the submitted bundle are never adopted as trust.
The task graph is valid and signedThe graph is validated at the bundle’s time and its signature verifies under a trusted issuer.
Delegation evidence is signedThe bundle carries signed swarm delegation evidence for the work it claims.
Routes and joins match the graphRoute plan receipts and join receipts refer to tasks the graph contains.
The budget pool fitsAllocations reconcile and the pool’s reservations stay within its total.
The revocation epoch is currentThe bundle is checked against the revocation epoch it claims.
Terminal receipts agreeTerminal graph receipts agree with the tasks, routes, joins, and allocations already validated.
Continuations bind the childEach continuation token is bound to the graph hash, its edge, route, join, and allocation.
Witness chains narrow scopeAlong each edge, the delegation witness chain shows the child’s scope narrowing from its parent’s.

The order runs from trust and graph integrity, through the records bound to the graph, to the continuation that binds a child to all of them. Any failed step refuses the bundle. The swarm admission path installs these checks on a host and lists their failure modes.

A treaty takes the stricter rule, one action class at a time

A finding, a guarded operation, and a jointly authorized purchase need different treatment. Each operator’s governance manifest classifies its action classes by mode and declares their co-signing, evidence, consistency, and quorum requirements. The modes are ordered:

ModeRankCoordination role
observation0Publish or assess a signal without treating it as a settled action.
guarded1Apply the configured checks to an action.
receipt_backed2Require the action’s declared receipt evidence.
partition_contingency3Use the declared contingency treatment for a network partition.
maintenance4Classify maintenance operations under their declared requirements.

A treaty combines the participants’ manifests into a ladder intersection. For each class it takes the strongest mode and the strongest co-signing requirement, unions the required evidence, and requires every participant to declare the same consistency model. A difference between two manifests does not by itself reject the class. An incompatibility does, and it rejects the whole intersection.

Ladder intersection of two participants over three action classesper cellmodeco_signevidence_requiredkernel.buyerkernel.vendorintersection
max⁡k r(⋅)\htmlClass{role-seal}{\max_k\, r(\cdot)}
whisker.pheromone_depositobservation0crdt-commutativeguarded1crdt-commutativeguarded1= crdt-commutativeworkflow.cross_kernel.read_refund_caseguarded1totally-orderedreceipt_backed2totally-orderedreceipt_backed2= totally-orderedworkflow.destructive.vendor_calldestructiveguarded1totally-orderedreceipt_backed2destructivetotally-orderedreceipt_backed2≥ receipt_backed= totally-ordered
ma=max⁡k r(ma,k)κa,k=κa,1,da⇒r(ma)≥r(receipt_backed)\begin{aligned} &\htmlClass{role-seal}{m_a} = \max_k\, r(\htmlClass{role-call}{m_{a,k}}) \\ &\kappa_{a,k} = \kappa_{a,1}, \quad \htmlClass{role-grant}{d_a} \Rightarrow r(\htmlClass{role-seal}{m_a}) \ge r(\text{receipt\_backed}) \end{aligned}
max⁡(1, 2)=2ma=receipt_backed\begin{aligned} &\max(1,\, 2) = 2 \\ &\htmlClass{role-seal}{m_a = \text{receipt\_backed}} \end{aligned}
LadderIntersection3 action classes, each reduced to one combined declaration
Ladder intersection of two participants over three action classesmodeco_signevidence_requiredwhisker.pheromone_depositkernel.buyerobservation0crdt-commutativekernel.vendorguarded1crdt-commutativeintersectionguarded1workflow.cross_kernel.read_refund_casekernel.buyerguarded1totally-orderedkernel.vendorreceipt_backed2totally-orderedintersectionreceipt_backed2workflow.destructive.vendor_calldestructive, declared by kernel.vendorkernel.buyerguarded1totally-orderedkernel.vendorreceipt_backed2totally-orderedintersectionreceipt_backed2≥ receipt_backed

One LadderIntersection over 3 action classes.

One LadderIntersection over 3 action classes.

ma=max⁡k r(ma,k)κa,k=κa,1da⇒r(ma)≥2\begin{aligned} &\htmlClass{role-seal}{m_a} = \max_k\, r(\htmlClass{role-call}{m_{a,k}}) \\ &\kappa_{a,k} = \kappa_{a,1} \\ &\htmlClass{role-grant}{d_a} \Rightarrow r(\htmlClass{role-seal}{m_a}) \ge 2 \end{aligned}
In Rust · treaty.rs:27634 lines
for participant in &treaty_scope.participant_kernel_ids {
    // … find this participant's action class
    let rank = ladder_mode_rank(&action.mode)?;
    if rank > mode_rank {
        mode_rank = rank;
        mode.clone_from(&action.mode);
    }
    destructive |= action.destructive;
    if let Some(existing) = consistency_model.as_ref() {
        if existing != &action.consistency_model {
            return rejected(
                "chio_federation_ladder_consistency_mismatch",
                "governance ladder consistency models do not intersect",
            );
        }
    } else {
        consistency_model = Some(action.consistency_model.clone());
    }
    if co_sign_requirement_rank(&action.co_sign)? > co_sign_requirement_rank(&co_sign)? {
        co_sign.clone_from(&action.co_sign);
    }
    // … merge the co-sign quorum
    for item in &action.evidence_required {
        evidence_required.insert(item.clone());
    }
    // … record participant_modes
}
if destructive && mode_rank < ladder_mode_rank("receipt_backed")? {
    return rejected(
        "chio_federation_ladder_destructive_below_floor",
        "intersected destructive action resolves below receipt backed mode",
    );
}
// … then the destructive crdt-commutative check
Two participants from the treaty tests with illustrative declarations. Modes and co-signing combine by rank maximum and evidence by union; consistency models must be equal, and any rejection stops the whole intersection. Each manifest also enforces its own destructive floor, receipt_backed here. Co-sign quorums are merged but not drawn.

Agree a treaty from your code, and each kernel computes the intersection on its own from the treaty scope and both manifests. The CLI runs the same computation, so either operator can check the other’s result:

partners.tspartners.pysrc/main.rs · in mainTerminal
import { Computer, USD } from "@chio-protocol/sdk";

const project = await Computer.open("quickstart");
const partner = await Computer.connect("partner");
const treaty = await project.treaty({
  partner,
  actions: ["workspace.read", "workspace.write", "model.invoke", "merge"],
  expiresIn: "7d",
});
console.log(treaty.describe());
Output
treaty           trt-01aa…4c19, signed by both kernels
participants     you · partner (did:chio:z6MkpA7…c2T)
action           you       them      admitted as
workspace.read   allow     allow     either kernel admits under its own policy
workspace.write  allow     allow     either kernel admits under its own policy
model.invoke     allow     allow     charged at your door
merge            co-sign   co-sign   both kernels sign
expires          in 7 days

Your manifest declares merge as guarded and your partner’s declares it receipt_backed, so the treaty takes the stronger mode and requires both kernels’ signatures on every merge. Destructive actions have two further floors: a destructive class that resolves below receipt_backed is rejected, and so is one that would use crdt-commutative consistency. The manifests’ hashes must match those the treaty scope names, and the trust handshake exchanges only the manifest references.

Classes that require n_of_m co-signing use FROST threshold authorization under a pinned roster and epoch, and the strict bilateral verifier requires the corresponding chio.frost.authorization.v1 artifact. The governance ladder lists the action-class declarations, and FROST quorum covers threshold verification.

A shared pool caps the whole fan-out

A swarm can divide one budget among parallel specialists, with each child task holding an allocation from the swarm’s pool. The pool enforces two invariants: every allocation accounts for all of its units, and the units reserved across the pool cannot exceed its total.

maxa=reserveda+activea+consumeda+releaseda+reverseda∑areserveda≤total\begin{aligned} &\mathrm{max}_a = \mathrm{reserved}_a + \mathrm{active}_a + \mathrm{consumed}_a \\ &\qquad + \mathrm{released}_a + \mathrm{reversed}_a \\[8pt] &\textstyle\sum_a \mathrm{reserved}_a \le \mathrm{total} \end{aligned}

Grant your partner’s agents a fork under the treaty with one five-dollar pool, two levels of delegation, and four children per parent, then send them issue #417:

partners.tspartners.pysrc/main.rs · in main
const fork = await project.fork();
const authority = fork.grant({
  to: partner,
  under: treaty,
  resources: { "/workspace": ["read", "write"], "/models/default": ["invoke"] },
  delegation: { depth: 2, fanout: 4 },
  budget: USD(5),
  expiresIn: "30m",
});
const run = await partner.exec("fix issue #417", { on: fork, authority });
for await (const event of run.events()) console.log(event.summary);
Output
00:00  them  admitted   "fix issue #417" under trt-01aa…4c19, pool USD 5.00
00:01  them  reserve    triage-417 1.00 · fixer-417 2.50 · halden-tester 1.00
00:02  them  refused    reserve 1.00 for fixer-2: swarm budget fanout exceeds pool total
00:02  them  reserve    fixer-2 0.50, pool reserved 5.00 of 5.00
00:44  them  sealed     fork c4e81b7 at 9e1f03a
spent  USD 3.62 of 5.00 across 4 of their agents

Their coordinator stayed inside the fan-out of four, and the pool still refused its fourth reservation: USD 5.50 reserved against a USD 5.00 total. A violation is refused by name. An allocation whose units do not add up fails with a rollup mismatch, and a pool reserved past its total fails with swarm budget fanout exceeds pool total.

The pool is the swarm’s shared accounting. Each child’s grant is still bounded by its own parent, individually; Budgets and metering covers those per-grant bounds, and Sub-agent budgets covers the pool’s full lifecycle.

Joint actions carry both signatures

When two organizations must commit to the same action, both kernels sign it. Apply your partner’s fix under the treaty, and the merge receipt comes back with your kernel’s signature and your partner’s:

partners.tspartners.pysrc/main.rs · in main
const merged = await project.apply(fork.changes, { treaty });
console.log(merged.receipt.describe());
Output
merge      main 2f60a91 to 9e1f03a, under trt-01aa…4c19
receipt    rcpt-01ab…f2d0, signed by your kernel 4f1c…a2e9
co-signed  partner kernel 9b07…d31c, over the same canonical body
verified   both signatures, against the keys you pinned

Both kernels sign the same canonical body: the receipt as canonical JSON, together with both kernel identifiers. The receipt’s own signature is unchanged, so a classic verifier can still check it alone. A federation-aware verifier also checks both detached signatures, and one operator’s signature cannot stand in for the other’s.

Co-signed receiptChoose what arrives.
canonical JSONσρσBσAρreceiptrcpt-01ab…f2d0bcanonical bodyCoSigningBodyK′partner kernel9b07…d31cKyour kernel4f1c…a2e9checks, in orderclassicfederation1
schema=dual-signed v1\text{schema} = \text{dual-signed v1}
2
(idA, idB)=pinned peers(\mathit{id}_A,\, \mathit{id}_B) = \text{pinned peers}
3
VfkA(b, σA)\mathrm{Vf}_{k_A}(b,\, \sigma_A)
4
VfkB(b, σB)\mathrm{Vf}_{k_B}(b,\, \sigma_B)
5
Vfκ(ρ, σρ)\mathrm{Vf}_{\kappa}(\rho,\, \sigma_\rho)
6
κ=kB\kappa = k_B
classic acceptsfederation accepts
canonicalσρσBσAρreceiptbcanonicalbodyK′partner kernel9b07…d31cKyour kernel4f1c…a2e9checks, in orderclassicfederation1
schema=dual-signed v1\text{schema} = \text{dual-signed v1}
2
(idA, idB)=pinned peers(\mathit{id}_A,\, \mathit{id}_B) = \text{pinned peers}
3
VfkA(b, σA)\mathrm{Vf}_{k_A}(b,\, \sigma_A)
4
VfkB(b, σB)\mathrm{Vf}_{k_B}(b,\, \sigma_B)
5
Vfκ(ρ, σρ)\mathrm{Vf}_{\kappa}(\rho,\, \sigma_\rho)
6
κ=kB\kappa = k_B
classic acceptsfederation accepts

Both kernels signed the same bytes

The federation-aware verifier rebuilds b from the receipt and the two kernel ids, checks your partner’s signature and yours against the keys you pinned, then checks the receipt’s own signature and that the tool host made it. A classic verifier reads only the last two rows and accepts the same file as a receipt from your kernel. Independent verification.

One co-signed merge receipt as two verifiers read it. b is the canonical CoSigningBody: the cosigning schema, the receipt as canonical JSON, and both kernel ids; κ is the receipt’s kernel_key. Kernel ids and keys are the Quickstart’s. The DSSE envelope and its predicate checks are left out.Bilateral Receipts

For an invocation, Chio verifies a bilateral DSSE envelope with both parties’ signatures over one canonical payload. The strict verifier checks the expected peer keys and the bound authorization context, and the predicate identifier chio.bilateral-cosign-invocation.v1 names the format. Bilateral co-sign covers the envelope, the signature checks, and the evidence requirements.

An auditor checks both kernels’ signatures offline

Export the shared history, and anyone holding the two kernel keys can verify it without access to either operator’s systems:

partners.tspartners.pysrc/main.rs · in mainTerminal
const evidence = await project.history.export("audit/");
console.log(await evidence.verify());
Output
treaty     trt-01aa…4c19, ladders intersect as signed
receipts   48 signed decisions from 2 kernels
co-signed  1 merge, rcpt-01ab…f2d0: both kernel signatures verify
refusals   1: reserve 1.00 for fixer-2, pool total
spend      USD 3.62 of the 5.00 pool
verified   offline, against both pinned kernel keys

Each co-signed receipt in the package is one record. The receipt your kernel signed sits whole inside body, and the two kernel signatures sit beside it:

audit/receipts.ndjson · merge receiptjson
{
  "schema": "chio.federation-dual-signed-receipt.v1",
  "body": {
    "id": "rcpt-01ab…f2d0",
    "tool_server": "quickstart",
    "tool_name": "merge",
    "decision": { "verdict": "allow" },
    "kernel_key": "4f1c…a2e9",
    "signature": "6b1d…94e0"
  },
  "orgAKernelId": "kernel.halden.example",
  "orgBKernelId": "kernel.larkfield.example",
  "orgASignature": "51f0…a3c8",
  "orgBSignature": "e7b2…16d9"
}

A verifier that reads only body, such as chio receipt verify --trusted-kernel-pubkey 4f1c…a2e9, accepts it as a receipt from your kernel. Only a verifier that checks orgASignature against your partner’s pinned key learns that your partner signed the same bytes.

A swarm also needs a fast signal path

Agents can contribute observations without making every observation a consensus decision. A PheromoneDeposit is a signed coordination signal whose concentration decays with time. A query aggregates live deposits for a subject class and namespace, applying peer weights and the configured newcomer discount.

Three pheromone deposits decaying over time, with their weighted sum10.10.01012345678evaporation floor φ = 0.01t (h)s(t)log scaleAllamaworksBdatacoCnewcoTt = 5.50 hwB = 0.60peer weight of dataco00.51dC = 2/8epoch age of newco12345678e − eC + 1
si(t)=ci⋅2−(t−ti)/hi\htmlClass{role-call}{s_i}(t) = c_i \cdot 2^{-(t - t_i)/h_i}
T(t)=∑i : si(t) ≥ φisi(t) wi di\htmlClass{role-seal}{T}(t) = \sum_{i \,:\, s_i(t) \,\ge\, \varphi_i} \htmlClass{role-call}{s_i}(t)\, \htmlClass{role-grant}{w_i}\, \htmlClass{role-grant}{d_i}
di=min⁡ ⁣(e−ei+1H, 1),  H=8\htmlClass{role-grant}{d_i} = \min\!\left(\tfrac{e - e_i + 1}{H},\ 1\right),\ \ H = 8
T(5.50)=0.018+0.126⋅0.60+0.050⋅0.25=0.106\begin{aligned} \htmlClass{role-seal}{T}(5.50) &= 0.018 \\ &\quad + 0.126 \cdot \htmlClass{role-grant}{0.60} \\ &\quad + 0.050 \cdot \htmlClass{role-grant}{0.25} \\ &= \htmlClass{role-seal}{0.106} \end{aligned}
Three pheromone deposits decaying over time, with their weighted sum10.10.0102468evaporation floor φ = 0.01ts(t)log scaleAllamaworksBdatacoCnewcoTt = 5.50wB = 0.6000.51dC = 2/813578
T(5.50)=0.018+0.076+0.013=0.106\htmlClass{role-seal}{T}(5.50) = 0.018 + 0.076 + 0.013 = \htmlClass{role-seal}{0.106}

At hour 5.5, the weighted concentration is 0.106. 3 of 3 deposits count.

si(t)=ci⋅2−(t−ti)/hiT(t)=∑i : si(t) ≥ φisi(t) wi didi=min⁡ ⁣(e−ei+1H, 1),  H=8\begin{aligned} \htmlClass{role-call}{s_i}(t) &= c_i \cdot 2^{-(t - t_i)/h_i} \\[6pt] \htmlClass{role-seal}{T}(t) &= \sum_{i \,:\, s_i(t) \,\ge\, \varphi_i} \htmlClass{role-call}{s_i}(t)\, \htmlClass{role-grant}{w_i}\, \htmlClass{role-grant}{d_i} \\[6pt] \htmlClass{role-grant}{d_i} &= \min\!\left(\tfrac{e - e_i + 1}{H},\ 1\right),\ \ H = 8 \end{aligned}
In Rust · substrate.rs:21312 lines
let strength = strength_at(deposit, now_unix_ms);
if let Some(floor) = deposit.body.evaporation_floor {
    if strength < floor {
        continue;
    }
}
let weight = peer_weight(&deposit.body.kernel_id, reputation_epoch);
// … WeightOutOfRange unless 0.0 <= weight <= 1.0
let discount = newcomer_discount_for_deposit(
    // … deposit, context, epoch, subject class
)?;
total_strength += strength * weight * discount;
Three illustrative deposits on one subject class; A uses the fixture’s confidence 0.82 and one-hour half-life, and hours are illustrative. A strong signal does not become a receipt, a grant, or reputation evidence, and its decay says nothing about whether the observation was true.

Strength halves with every half-life. A deposit below its evaporation floor drops out of the sum. Each remaining deposit is scaled by its peer’s weight and by a newcomer discount that grows toward one as the depositing passport ages in reputation epochs.

The depositing agent signs with its PASSPORT signing key. The deposit is neither a kernel receipt nor an Agent Passport credential. Expiry and admission limits keep stale or excessive deposits from accumulating indefinitely; they do not establish that an observation is true.

A signed pheromone policy can use a query report and peer weights in a runtime admission decision. That is an explicit policy path: a strong signal does not become a receipt, a grant, or reputation evidence by itself. The pheromone store covers decay, quotas, and queries.

Transport carries the relationship; each side configures enforcement

The optional iroh transport uses QUIC and domain-separated endorsements. It authenticates transport keys; treaty scope, authorization, quorum, and revocation checks belong above that connection. Add your partner’s card with --transport iroh, and your code reaches it by name:

partners.tspartners.pysrc/main.rs · in mainTerminal
const partner = await Computer.connect("partner");
console.log(partner.describe());
Output
partner    partner, did:chio:z6MkpA7…c2T
kernel     9b07…d31c, pinned from its card
transport  iroh, authenticated at handshake

Selecting the transport opens the connection. The treaty, the scope it grants, and the receiving operator’s trust anchors are configured separately, by each side.

Independent operators can combine these mechanisms for shared observations, bounded subcontracting, jointly authorized actions, and exchangeable evidence. Markets for agent work adds a delivery-and-payment model for agent-produced work. Each deployment still connects its own admission, transport, policy, and storage paths; a signed format does not by itself establish that integration end to end.

Follow the mechanism you need