Chio/Docs
LOGIN · JOIN

Two-Org Worked Example

Let a partner's agent call one of your tools across the boundary, with your kernel admitting the call under your own policy.

Guide chio@5f63e6e

On this page

Your partner's fixer-2, working your issue #417, prices its candidate fix through your pricing.quote tool under a USD 5.00 budget. Each operator keeps its own keys, policy, and evidence, and your kernel admits every call from the partner at your door under your policy.

Start

One run pins the partner, issues its agent a capped grant, and admits its first call

Add the partner's card, record what you admit from it, issue its agent a capped capability, and read the receipt of its first call.

Terminalbash
chio partner add partner-card.json --name partner
chio trust federation-policy upsert --input ./partner-admission.json \
  --federation-policies-file ./federation-policies.json
chio trust federated-issue --presentation-response presentation.json \
  --challenge challenge.json --capability-policy pricing-quote.yaml \
  --delegation-policy delegation-policy.json \
  --upstream-capability-id cap-01b7…e2a4
chio receipt list --capability cap-01b9…40c7 --limit 1
Output
partner    did:chio:z6MkpA7…c2T, named partner
kernel     9b07…d31c, pinned from its card
policy     fap-pricing-001 stored for namespace pricing.larkfield.example
issued     cap-01b9…40c7 to fixer-2: pricing.quote, budget USD 5.00
anchor     cap-01b8…5d13, bridged to upstream cap-01b7…e2a4
receipt    rcpt-01b9…a61e  allow  pricing.quote sku-301 at 15%, signed by your kernel

You should see the partner's kernel key pinned from its card, your admission policy stored, a child capability issued for pricing.quote with its lineage anchor, and an allowed receipt your kernel signed.

Possession at the doorChoose what arrives.
Their operatorYour operatoraK′Kdρtheir agenttheir kernelyour kerneltoolTheir operatorYour operatoraK′Kdρagenttheir kernelyour kerneltool

κgrantσpossessiongguardsτtreatyBbudget

The proof binds the grant to this call

Their agent presents the federated grant with a DPoP proof signed by the key the grant names. Your kernel verifies the grant, compares each of the proof's bindings with the request it received, then runs your guards, the treaty check, and your budget. The proof's nonce is consumed at dispatch, and your kernel signs the receipt. Proof of possession.

One federated call at your kernel's door: the grant and DPoP proof their agent presents, the proof's five bindings, and the checks your kernel runs under its own policy, in order. Scope runs inside the grant check. Output checks after dispatch and the durable replay profile are left out.Proof of possession

Three decisions stay separate. Pinning the partner's key establishes whose signatures you can check. Issuing a child capability establishes what its holder may request. Your kernel's admission decides whether this call proceeds, and a relationship with one partner carries no authority to a third operator.

The card's key is the anchor the handshake answers to

Adding the partner's card pins its kernel key. Embedding the kernel yourself, the same pin is a trust anchor on KernelTrustExchange: each side signs a challenge addressed to the other and verifies the reply against its anchor.

federation.rs · embedding the kernelrust
use chio_federation::trust_establishment::KernelTrustExchange;

// The partner's card supplies its kernel id and key: the trust anchor.
let exchange = KernelTrustExchange::new("kernel.larkfield.example", your_keypair)
    .with_trusted_peer("kernel.halden.example", partner_public_key.clone());

let now = unix_seconds_now();
let envelope = exchange.local_envelope("kernel.halden.example", "nonce-1", now)?;
// Send `envelope` over your transport; receive the partner's.
let peer = exchange.accept_envelope(&envelope_from_partner, "kernel.halden.example", now)?;
assert!(peer.is_fresh(now)); // true until rotation_due

accept_envelope delegates to accept_envelope_with_policy with a default QuorumPolicy and checks, in order:

  1. Signature. envelope.verify_signature() verifies the canonical-JSON challenge bytes under the declared key.
  2. Address. challenge.remote_kernel_id MUST equal your kernel id, or AddressMismatch.
  3. Sender. challenge.local_kernel_id MUST equal the expected peer id, or KernelIdMismatch.
  4. Clock. The envelope timestamp MUST sit within max_handshake_skew_secs of your clock, five minutes by default, or ClockSkewExceeded.
  5. Anchor. The declared key MUST match the anchor or the existing pin. No anchor is MissingTrustAnchor; a different key is UnexpectedPeerKey, carrying the expected and actual hex.
  6. Tier. The peer's conformance_tier MUST meet the quorum policy's min_tier, Bronze by default, or ConformanceTierBelowMinimum before any peer is constructed.
  7. Pin. The key is pinned as a fresh FederationPeer with rotation_due = now + rotation_window_secs, twelve hours by default.

First contact succeeds only against an anchor, which is what the card supplies. The run's kernel 9b07…d31c, pinned from its card line is this pin.

Your kernel resolves a pinned peer fresh or refuses it

The accepted peer goes into the kernel that resolves calls, beside the local kernel id it advertises and the cosigner it uses for bilateral receipts.

federation.rs · embedding the kernelrust
let mut kernel = ChioKernel::new(config).with_federation_peers(vec![peer]);
kernel.set_federation_local_kernel_id("kernel.larkfield.example");
kernel.set_federation_cosigner(cosigner); // asks the partner's kernel to co-sign

federation_peer(remote_kernel_id, now) returns None for an unknown peer and for one past its rotation_due, so a stale pin fails closed at the lookup and every federation operation that depends on it refuses. The deadline is the accepting kernel's own clock plus the rotation window; the envelope timestamp is checked for skew and leaves the window alone. The local kernel id defaults to the hex encoding of the signing public key.

Your admission policy decides what a partner may enter

chio trust federation-policy upsert stores a FederationAdmissionPolicyRecord wrapping a signed FederatedOpenAdmissionPolicyArtifact, scoped to a federation namespace and shared by every partner that enters it. This one admits bond-backed participants, requires a USD 50.00 listing bond and explicit local review, and keeps an unactivated partner visibility-only.

partner-admission.jsonjson
{
  "schema": "chio.permissionless-federation-policy.v1",
  "publishedAt": 1714291200,
  "policy": {
    "body": {
      "schema": "chio.federation-open-admission-policy.v1",
      "policyId": "fap-pricing-001",
      "issuedAt": 1714291200,
      "namespace": "pricing.larkfield.example",
      "governingOperatorId": "larkfield",
      "allowedAdmissionClasses": ["bond_backed"],
      "stakeRequirements": [
        {
          "admissionClass": "bond_backed",
          "requiredBondClass": "listing",
          "minimumBondAmount": { "units": 5000, "currency": "USD" },
          "slashable": true,
          "governanceCaseRequired": true
        }
      ],
      "governingCharterRef": {
        "kind": "governance_charter",
        "schema": "chio.registry.governance-charter.v1",
        "artifactId": "chr-2026-001",
        "operatorId": "larkfield",
        "sha256": "6a0f…c2d1"
      },
      "feeScheduleRef": {
        "kind": "open_market_fee_schedule",
        "schema": "chio.registry.market-fee-schedule.v1",
        "artifactId": "fee-2026-001",
        "operatorId": "larkfield",
        "sha256": "b83e…19f4"
      },
      "explicitLocalReviewRequired": true,
      "visibilityOnlyWithoutActivation": true
    },
    "signerKey": "4f1c…a2e9",
    "signature": "0d7c…e5a8"
  }
}

The run's policy fap-pricing-001 stored line is this record. A per-pair trust activation exchange attaches scope, delegation, and import controls between two operators, and the default FederationImportControl admits no runtime traffic until you opt in. Receipt sharing has its own signed record, made by chio evidence federation-policy create for one issuer and partner pair.

Your trust control issues a child capability within the partner's ceiling

chio trust federated-issue calls POST /v1/federation/capabilities/issue on your trust control plane. It verifies the agent's passport presentation against the expected challenge, then mints a child capability for pricing.quote on pricing.larkfield.example with a USD 5.00 ceiling. The service enforces three rules:

  1. When upstreamCapabilityId is supplied, a delegation policy bound to that exact parent capability id MUST also be present.
  2. When a delegation policy is supplied, the requested child capability MUST NOT exceed the policy ceiling.
  3. The trust-control service MUST reject untrusted delegation-policy signers.

Issuance persists two records into capability lineage: a local delegation anchor, returned as delegationAnchorCapabilityId, and a child snapshot rooted at it. With an upstream capability, the anchor also bridges to the imported parent, so querying your receipt and lineage stores for the child walks back through the anchor to the partner's capability. An auditor verifies that chain without contacting either kernel.

Your kernel checks the grant, the proof, and your policy before it dispatches

The partner's agent presents the child capability with a DPoP proof, and federated grants require that proof at your door. Your kernel verifies the capability under your trusted capability issuers, configured separately from peer pins. It compares each of the proof's bindings with the request it received, then runs your guards, the treaty check against the scope and intersection in your own store, and your budget.

The proof's nonce is consumed at dispatch and your kernel signs the receipt. A grant presented with another agent's proof fails with DPoP proof verification failed: agent_key does not match capability subject (sender constraint violated), and a stale treaty records chio_treaty_stale in chio_runtime.failure_code. With the cosigner and an artifact store installed, the two kernels complete a bilateral attestation over the same receipt. Read admission, the tool outcome, and bilateral completion as separate facts, and keep the receipt and the artifact the downstream verifier needs.

Read more in Bilateral receipts and in Cross-operator delegation, which signs the delegation policy that caps a partner's requests.

A swapped key or an expired pin stops the relationship before any call

Present an envelope signed by a key the card never named, then resolve the peer after its rotation deadline.

federation.rs · embedding the kernelrust
// The partner signs with a key its card never named.
let refused = exchange.accept_envelope(&envelope_with_other_key, "kernel.halden.example", now);
println!("{}", refused.unwrap_err());

// Twelve hours later the pin has reached rotation_due.
let stale = exchange.resolve("kernel.halden.example", now + 12 * 60 * 60);
println!("{}", stale.unwrap_err());
Output
peer kernel.halden.example declared unexpected public key; expected 9b07…d31c, got 5e21…0a7f
peer kernel.halden.example is stale and must be re-handshaked before use

You should see both refusals, each before any peer state changes. Peer freshness, key trust, and delegated authority change on separate lifecycles, and repeating issuance or dispatch repairs none of them:

  1. Renew a stale peer. Run a new signed exchange and install the accepted peer in the runtime that resolves calls. KernelTrustExchange::resolve refuses a stale record, and updating a separate exchange store leaves the kernel's installed peer set as it was.
  2. Follow a key change. A rotation that extends the authority key history on the partner's card keeps the pin, and receipts signed under the previous key still verify. Any other key reaches your trust store only through a new card; a valid signature from it is UnexpectedPeerKey.
  3. Withdraw access. Apply the withdrawal to the runtime's peer and admission configuration and to capability revocation. KernelTrustExchange::forget removes a pin from that exchange's store only, and propagating the change to active kernels is the deployment's job. Keep past public keys and artifacts for audit, and use the revocation feed to keep receiving verifiers current.

For treaty-governed work, a changed participant manifest needs a scope bound to the new manifest digest, a recomputed intersection, and a new admission decision. Follow the contract-change procedure before the affected action.

Extend it: bring in a third operator

Trust stays local to each pair. Identity is the Ed25519 kernel keypair on each side, there is no global identity provider, and a verifier refuses a receipt whose issuing kernel id it has not pinned. To call a third operator's tools, add its card and run a separate handshake; a compromised key affects only the counterparties that trust it, and each manages its own response.

Each side sets its own per-pair terms in local configuration: peer keys, rotation windows, handshake skew, autonomy ceilings, evidence freshness, import controls, and re-export policy. For work all three operators share, a treaty names every participant and each kernel folds every ladder; a co-sign quorum larger than two uses FROST threshold signatures.

Read more in Cross-org swarms, which binds a remote specialist's graph hop to its task, route, and shared action contract.

Handshake records

The handshake body is HandshakeChallenge at chio-federation/src/trust_establishment.rs:229, schema chio.federation-kernel-handshake.v1.

crates/trust/chio-federation/src/trust_establishment.rsrust
pub const FEDERATION_HANDSHAKE_SCHEMA: &str = "chio.federation-kernel-handshake.v1";

// ...

/// Challenge body signed by one kernel during the handshake.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct HandshakeChallenge {
    pub schema: String,
    pub local_kernel_id: String,
    pub remote_kernel_id: String,
    pub nonce: String,
    pub timestamp: u64,
    #[serde(default, skip_serializing_if = "is_default_capability_negotiation")]
    pub capabilities: CapabilityNegotiation,
    #[serde(default, skip_serializing_if = "is_default_conformance_tier")]
    pub conformance_tier: ConformanceTier,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ladder_manifest_ref: Option<LadderManifestRef>,
}

The last three fields carry capability negotiation and conformance-ladder metadata. Each is skip_serializing_if at its default, so a minimal challenge serializes to the five required fields. Each kernel signs the canonical-JSON encoding of its challenge and wraps it in a PeerHandshakeEnvelope (trust_establishment.rs:312).

crates/trust/chio-federation/src/trust_establishment.rsrust
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PeerHandshakeEnvelope {
    pub challenge: HandshakeChallenge,
    pub declared_public_key: PublicKey,
    pub signature: Signature,
}

Source lines: accept_envelope at trust_establishment.rs:749, local_envelope at :706, verify_signature at :444, with_trusted_peer at :666, DEFAULT_HANDSHAKE_MAX_SKEW_SECS = 5 * 60 at :57, and DEFAULT_ROTATION_WINDOW_SECS = 12 * 60 * 60 at :52.

The federation peer set

ChioKernel exposes these methods around the pinned set, defined in chio-kernel/src/kernel/construction.rs. The setter is the builder with_federation_peers, the resolver is federation_peer(remote_kernel_id, now), and the local-id setter is the singular set_federation_local_kernel_id.

MethodPurposeSource
with_federation_peers(self, peers: Vec<FederationPeer>) -> Self
Builder; installs the trusted peer set during kernel construction and replaces any prior set. Marked #[must_use].
construction.rs:1152
set_federation_cosigner(&mut self, cosigner)
Installs the bilateral cosigner that asks a peer kernel for a co-signature. Tests use InProcessCoSigner; production uses an mTLS RPC client.
construction.rs:1264
set_federation_local_kernel_id(&self, id)
Advertises this kernel's stable id, such as a DNS name, to remote peers. Defaults to the hex encoding of the signing public key.
construction.rs:1299
federation_peer(&self, remote_kernel_id, now) -> Option<FederationPeer>
Resolves a peer; returns None when unknown or when !peer.is_fresh(now).
construction.rs:1306
federation_peers_snapshot(&self) -> Vec<FederationPeer>
Cloned snapshot of every pinned peer.
construction.rs:1321

The pinned peer record is FederationPeer:

crates/trust/chio-federation/src/trust_establishment.rs192-216rust
/// Pinned federation peer entry.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct FederationPeer {
    pub kernel_id: String,
    pub public_key: PublicKey,
    /// Cross-surface conformance tier that was signed into the peer's most
    /// recent accepted handshake.
    #[serde(default)]
    pub conformance_tier: ConformanceTier,
    /// Unix seconds at which the peer was last pinned via a successful
    /// handshake.
    pub established_at: u64,
    /// Unix seconds at which the pin expires. After this timestamp the
    /// peer is treated as stale and MUST be re-handshaked before any
    /// federation-level operation is accepted against it.
    pub rotation_due: u64,
    /// Peer-advertised protocol feature bitset. Missing on compatibility peers
    /// defaults to current capability semantics without optional features.
    #[serde(default)]
    pub capabilities: CapabilityNegotiation,
    /// Optional signed ladder manifest reference accepted during handshake.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ladder_manifest_ref: Option<LadderManifestRef>,
}

conformance_tier is the tier the peer signed into its accepted handshake, capabilities the negotiated protocol feature bitset, and ladder_manifest_ref the optional signed ladder reference. All three carry serde defaults, so a compatibility peer that omits them still pins. Freshness is one comparison, FederationPeer::is_fresh(now) -> now < self.rotation_due (trust_establishment.rs:221).

Trust-control routes and transport

The handshake is an exchange between the two kernels: one builds its envelope with local_envelope, and the other hands the bytes it received to accept_envelope. The trust control plane registers no handshake route. Its federation group, registered in chio-control-plane/src/trust_control/service_types/paths.rs lines 12 to 19, is these 7 paths:

PathMethodsHandlers
/v1/federation/capabilities/issue
POST
handle_federated_issue
/v1/federation/evidence-shares
GET
handle_shared_evidence_report
/v1/federation/open-admission-policies
GET
handle_list_federation_policies
/v1/federation/open-admission-policies/evaluate
POST
handle_evaluate_federation_policy
/v1/federation/open-admission-policies/{policy_id}
DELETE, GET, PUT
handle_get_federation_policy, handle_upsert_federation_policy, handle_delete_federation_policy
/v1/federation/providers
GET
handle_list_enterprise_providers
/v1/federation/providers/{provider_id}
DELETE, GET, PUT
handle_get_enterprise_provider, handle_upsert_enterprise_provider, handle_delete_enterprise_provider

The signature covers the canonical-JSON HandshakeChallenge bytes, so the transport supplies confidentiality and channel authentication and leaves handshake authenticity to the signature. Cross-organization traffic runs over HTTPS with mTLS or signed bodies by default. The optional iroh lane mounts each lane on its own ALPN over QUIC through self-hosted relays; its co-sign lane is chio/federation/bilateral-dsse-cosign/1 (chio-federation-transport-iroh/src/lanes/bilateral.rs:88) and carries co-signature requests between peers that are already pinned.

Open-admission policy fields

chio trust federation-policy manages chio.federation-open-admission-policy.v1 with the verbs list, get, upsert, delete, and evaluate; upsert reads --input <json-file> and writes an optional local --federation-policies-file <path>. The record is FederatedOpenAdmissionPolicyArtifact (chio-federation/src/open_admission.rs).

FieldHoldsPurpose
policy_id
id
Stable id of the admission policy record
namespace
namespace
Federation namespace the policy governs
governing_operator_id
operator id
Operator that owns and signs the policy
allowed_admission_classes
list
Classes a peer may enter: public_untrusted, reviewable, bond_backed, role_gated
stake_requirements
list
Per class: bond class, minimum amount, slashability, governance case
governing_charter_ref
reference
The charter that authorizes the policy
fee_schedule_ref
reference
The open-market fee schedule that prices participation
explicit_local_review_required
boolean
Local review precedes admission
visibility_only_without_activation
boolean
A peer stays visibility-only until explicit local activation

Activation exchange record

A per-pair activation is FederationActivationExchangeArtifact at chio-federation/src/activation.rs:19:

crates/trust/chio-federation/src/activation.rsrust
pub const CHIO_FEDERATION_ACTIVATION_EXCHANGE_SCHEMA: &str =
    "chio.federation-activation-exchange.v1";

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct FederationActivationExchangeArtifact {
    pub schema: String,
    pub exchange_id: String,
    pub issued_at: u64,
    pub expires_at: u64,
    pub source_operator_id: String,
    pub target_operator_id: String,
    pub listing_id: String,
    pub activation_ref: FederationArtifactReference,
    pub listing_ref: FederationArtifactReference,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub governing_charter_ref: Option<FederationArtifactReference>,
    pub scope: FederationTrustScope,
    pub delegation_control: FederationDelegationControl,
    pub import_control: FederationImportControl,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub note: Option<String>,
}

SignedFederationActivationExchange is an alias for SignedExportEnvelope<FederationActivationExchangeArtifact> (activation.rs:38). That envelope is rename_all = "camelCase", deny_unknown_fields over exactly three keys, body, signerKey, and signature (chio-core-types/src/receipt/lineage.rs:407-417), and a fourth key fails deserialization.

The schema id is hyphenated, chio.federation-activation-exchange.v1, and validate_federation_activation_exchange rejects any other value with UnsupportedSchema. An Ed25519 PublicKey and Signature serialize as bare lowercase hex with no algorithm prefix; the prefixed forms p256:, p384:, and hybrid: are reserved for other key material (chio-core-types/src/crypto.rs:581 and :791). The default FederationImportControl (chio-federation/src/artifacts.rs:63) is conservative across all five booleans.

The record below uses the serialized field names; a bracketed value marks a digest, key, or signature.

activation-exchange.jsonjson
{
  "body": {
    "schema": "chio.federation-activation-exchange.v1",
    "exchangeId": "fex-partner-001",
    "issuedAt": 1714291200,
    "expiresAt": 1716883200,
    "sourceOperatorId": "partner-operator",
    "targetOperatorId": "larkfield",
    "listingId": "listing-pricing-quote",
    "activationRef": {
      "kind": "trust_activation",
      "schema": "chio.registry.trust-activation.v1",
      "artifactId": "activation-77",
      "operatorId": "larkfield",
      "sha256": "91c4…7be0"
    },
    "listingRef": {
      "kind": "listing",
      "schema": "chio.registry.listing.v1",
      "artifactId": "listing-pricing-quote",
      "operatorId": "larkfield",
      "sha256": "2e6d…a013"
    },
    "scope": {
      "namespace": "pricing.larkfield.example",
      "subjectOperatorId": "larkfield",
      "allowedActorKinds": ["tool_server"],
      "allowedAdmissionClasses": ["bond_backed"]
    },
    "delegationControl": {
      "delegatorOperatorId": "partner-operator",
      "delegateOperatorId": "larkfield",
      "maxHops": 1,
      "attenuationRequired": true,
      "visibilityOnlyUntilLocalActivation": true
    },
    "importControl": {
      "explicitLocalActivationRequired": true,
      "manualReviewRequired": true,
      "rejectStaleInputs": true,
      "allowVisibilityWithoutRuntimeTrust": true,
      "prohibitAmbientRuntimeAdmission": true
    }
  },
  "signerKey": "4f1c…a2e9",
  "signature": "0d7c…e5a8"
}

Federated issuance fields

Request fields on POST /v1/federation/capabilities/issue, in addition to the standard issuance body:

FieldPresenceMeaning
presentation
required
Passport presentation proving federated subject identity
expectedChallenge
required
Challenge value the presentation must satisfy
capability
required
Requested issued capability body
delegationPolicy
optional
Signed ceiling for delegated issuance; untrusted signers are rejected
upstreamCapabilityId
optional
Imported parent capability anchor for multi-hop lineage

The success-response fields that carry the delegation, out of the wider FederatedIssueResponse (chio-control-plane/src/trust_control/service_types/requests.rs:50):

FieldPresenceMeaning
capability
always
Newly issued child capability
delegationAnchorCapabilityId
optional
Lineage anchor persisted by trust control
subjectPublicKey
always
Resolved subject key used for issuance

Handshake errors

The handshake failures are the 16 variants of PeerHandshakeError. Each carries the exact reason string it prints, and each is fail-closed: a caller refuses to pin a peer when any step fails.

crates/trust/chio-federation/src/trust_establishment.rs462-528rust
/// Errors raised by the trust-establishment primitives. Every variant is
/// fail-closed: callers MUST refuse to pin a peer when any step fails.
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum PeerHandshakeError {
    #[error("unsupported handshake schema: {0}")]
    UnsupportedSchema(String),

    #[error("canonical JSON encoding failed: {0}")]
    CanonicalJson(String),

    #[error("handshake signing failed: {0}")]
    SigningFailed(String),

    #[error("remote handshake signature is invalid")]
    InvalidSignature,

    #[error("capability negotiation failed closed: {0}")]
    CapabilityNegotiation(String),

    #[error("remote envelope is addressed to kernel_id {addressed_to} but we are {actual}")]
    AddressMismatch {
        addressed_to: String,
        actual: String,
    },

    #[error("remote envelope declares self as kernel_id {declared} but we expected {expected}")]
    KernelIdMismatch { declared: String, expected: String },

    #[error("remote envelope timestamp {envelope} drifts from local clock {local} beyond {skew}s")]
    ClockSkewExceeded {
        envelope: u64,
        local: u64,
        skew: u64,
    },

    #[error("peer {0} is not pinned; run a handshake before resolving")]
    PeerNotPinned(String),

    #[error("peer {0} is stale and must be re-handshaked before use")]
    PeerStale(String),

    #[error("peer {0} is not trusted for first contact; configure a trust anchor before accepting handshakes")]
    MissingTrustAnchor(String),

    #[error("peer {kernel_id} declared unexpected public key; expected {expected}, got {actual}")]
    UnexpectedPeerKey {
        kernel_id: String,
        expected: String,
        actual: String,
    },

    #[error("peer {kernel_id} conformance tier {actual:?} is below required {minimum:?}")]
    ConformanceTierBelowMinimum {
        kernel_id: String,
        actual: ConformanceTier,
        minimum: ConformanceTier,
    },

    #[error("invalid conformance evidence: {0}")]
    InvalidConformanceEvidence(String),

    #[error("invalid ladder manifest reference: {0}")]
    InvalidLadderManifestRef(String),

    #[error("trust store is poisoned and cannot service requests")]
    StorePoisoned,
}

These are the variants a bare retry leaves in place, each with the operator's action:

  • MissingTrustAnchor: first contact with no installed anchor. Add the partner's card (or call with_trusted_peer), then retry.
  • UnexpectedPeerKey: the declared key differs from the anchor or the existing pin; the variant carries both hex values. Re-pinning takes explicit operator action.
  • ClockSkewExceeded: the envelope timestamp is further from the local clock than max_handshake_skew_secs. Check NTP on both kernels and re-issue the envelope.
  • PeerStale and PeerNotPinned: resolve(kernel_id, now) found a pin past rotation_due, or no pin (trust_establishment.rs:839). Run the handshake again.
  • ConformanceTierBelowMinimum: the peer's negotiated tier is below the quorum policy's min_tier. Raise the peer's tier or lower the local floor.
  • StorePoisoned: the peer trust store lock is poisoned after a panic. Restart the affected service.

Issuance has one failure of its own: a federated issuance carrying a delegation policy whose signer is outside the local trusted issuer set is rejected at POST /v1/federation/capabilities/issue (spec/WIRE_PROTOCOL.md §4.2).

Federated receipt tables

Imported evidence and cross-org lineage land in three SQLite tables created inline by chio-store-sqlite/src/receipt_store/bootstrap/open.rs, lines 1412 to 1449:

crates/platform/chio-store-sqlite/src/receipt_store/bootstrap/open.rssql
CREATE TABLE IF NOT EXISTS federated_lineage_bridges (
    local_capability_id TEXT PRIMARY KEY
        REFERENCES capability_lineage(capability_id) ON DELETE CASCADE,
    parent_capability_id TEXT NOT NULL,
    share_id TEXT REFERENCES federated_evidence_shares(share_id)
);
CREATE INDEX IF NOT EXISTS idx_federated_lineage_bridges_parent
    ON federated_lineage_bridges(parent_capability_id);

CREATE TABLE IF NOT EXISTS federated_evidence_shares (
    share_id TEXT PRIMARY KEY,
    manifest_hash TEXT NOT NULL,
    imported_at INTEGER NOT NULL,
    exported_at INTEGER NOT NULL,
    issuer TEXT NOT NULL,
    partner TEXT NOT NULL,
    signer_public_key TEXT NOT NULL,
    require_proofs INTEGER NOT NULL DEFAULT 0,
    query_json TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_federated_evidence_shares_imported_at
    ON federated_evidence_shares(imported_at);

CREATE TABLE IF NOT EXISTS federated_share_tool_receipts (
    share_id TEXT NOT NULL
        REFERENCES federated_evidence_shares(share_id) ON DELETE CASCADE,
    seq INTEGER NOT NULL,
    receipt_id TEXT NOT NULL,
    timestamp INTEGER NOT NULL,
    capability_id TEXT NOT NULL,
    subject_key TEXT,
    issuer_key TEXT,
    raw_json TEXT NOT NULL,
    PRIMARY KEY (share_id, seq),
    UNIQUE (share_id, receipt_id)
);
CREATE INDEX IF NOT EXISTS idx_federated_share_receipts_capability
    ON federated_share_tool_receipts(capability_id);
CREATE INDEX IF NOT EXISTS idx_federated_share_receipts_subject
    ON federated_share_tool_receipts(subject_key);
  • federated_evidence_shares records each accepted import with issuer, partner, signer key, and the canonical query that produced it.
  • federated_share_tool_receipts stores raw imported receipt JSON keyed by (share_id, seq); (share_id, receipt_id) is uniquely indexed, so a duplicate import is idempotent.
  • federated_lineage_bridges links a locally minted delegation anchor in capability_lineage to its imported parent and the share that brought the parent across. ON DELETE CASCADE on the local capability id drops the bridge row with the local anchor and keeps the imported share.

Resolving a pinned peer

At dispatch time your kernel calls KernelTrustExchange::resolve(remote_kernel_id, now), which returns the pinned record and asserts freshness through FederationPeer::is_fresh(now). The record's three defaulted fields are absent from the wire when the peer left them at their defaults. The partner's pin, in serialized field names, with a 12-hour rotation window:

federation-peer.jsonjson
{
  "kernelId": "kernel.halden.example",
  "publicKey": "9b07…d31c",
  "establishedAt": 1745784000,
  "rotationDue": 1745827200
}

The pin is fresh (now < rotationDue), so the call proceeds, and the resolved key is the anchor a verifier uses to check the partner's detached signature on a dual-signed receipt. On a stale pin resolve returns PeerStale and the call is refused until a new handshake. What the operators agreed beyond the pin lives in the activation exchange: FederationTrustScope for namespace and actor kinds, FederationDelegationControl for hop count and attenuation, and FederationImportControl for what an import may do locally.

The procurement application runs two mediation edges under one shared trust authority, and Bilateral receipts defines the shared intent binding and the signatures a receipt pair carries. FROST threshold signing lives in chio-federation/src/frost/.

Next: co-sign the receipt both kernels keep