Chio/Docs
LOGIN · JOIN

Peer Handshake

Prove that a partner's kernel holds the key its card pinned before your kernel records it as a peer.

Concept chio@5f63e6e

On this page

Your kernel records a partner as a peer only after the partner's kernel signs a fresh challenge with the key its card pinned. The handshake runs that proof once in each direction and leaves a peer record with a conformance tier and a rotation deadline.

Adding a partner card with chio partner add pins the partner's kernel key. The handshake confirms it when the relationship opens and again before every rotation deadline, and a downloadable Rust program below runs one accepted handshake and three refusals.

Partner cardChoose what the card does.
Your operatorTheir operatorKK′Your kernelTheir kernelYour operatorTheir operatorKK′Your kernelTheir kernel

The handshake answers to the pin

Each kernel signs a challenge that binds both kernel ids, a fresh nonce, and the time. The receiver verifies the response against the pinned key, then records the peer's conformance tier and its rotation deadline beside the pin. Peer handshake.

The handshake between two partner kernels: the signed challenge your kernel checks against the key the partner card pinned, and a response under another key that stops before any peer state is written. Treaty terms and transport are left out.Peer pins

Each kernel signs a challenge the other checks against its pin

Each side builds a challenge under the schema chio.federation-kernel-handshake.v1 that binds its own kernel id, the remote kernel id, a fresh nonce, and the time, then signs it with its kernel key. The envelope it sends carries the challenge, the signature, and the declared public key.

  1. Signature. The signature must verify under the declared key, over the canonical JSON of the challenge.
  2. Address. The envelope must be addressed to this kernel, and its declared kernel id must be the peer the receiver expected.
  3. Freshness. The timestamp must sit within the accepted clock skew, five minutes by default.
  4. Pin. The declared key must match the trust anchor the partner card supplied or a peer already pinned. A different key stops with UnexpectedPeerKey, before any peer state is written.
  5. Terms. The peer's conformance tier must meet the receiver's floor, and both sides must share a protocol capability set. An optional signed ladder manifest reference names the governance ladder the peer enforces.

A handshake that passes every step records the peer as a FederationPeer: its kernel id, public key, accepted conformance tier, negotiated capabilities, ladder manifest reference, and rotation deadline. Keep that record with the pin; every later exchange with the partner resolves through it. chio partner status prints the record your kernel holds:

Terminalbash
chio partner status partner
Output
partner       did:chio:z6MkpA7…c2T, named partner
kernel        9b07…d31c, pinned from partner-card.json
handshake     verified both ways, chio.federation-kernel-handshake.v1
tier          silver, your floor bronze
capabilities  signals · revocation · co-sign
ladder        manifest 040f…bc01, signed by the partner
rotation      deadline in 11 h 52 min

Read more in Operator Identity & Key Bindings, which separates the kernel id, passport key, and endpoint the handshake and transport bind.

A pin lasts until its rotation deadline, and only a new handshake renews it

Every accepted handshake sets the peer's rotation deadline from the configured freshness window, twelve hours by default. After the deadline, resolving the peer fails closed with PeerStale until the two kernels run the handshake again. Nothing renews a pin silently. Run it before the deadline, and the record keeps its key and moves its deadline:

Terminalbash
chio partner handshake partner
Output
handshake  verified both ways with did:chio:z6MkpA7…c2T
key        9b07…d31c, matches the pin
tier       silver, your floor bronze
rotation   deadline in 12 h, renewed

The handshake re-proves a key; it never introduces one. When the partner rotates its authority key, the new key reaches your trust store through a new card or the authority key history the card carries, and receipts signed under previous keys still verify offline.

Read more in Peer Pins & Trust Bundles, which covers key persistence and rotation.

The handshake carries one conformance tier derived from the peer's evidence

The signed challenge carries the peer's conformance tier. ConformanceEvidence derives exactly one tier from three measurements: threat coverage and mutation kill rate in basis points, and the count of crates with Kani trust-boundary harnesses. The receiver compares the tier with its QuorumPolicy.min_tier floor, which defaults to Bronze.

  • Bronze. Schema-valid evidence is present, below the Silver thresholds.
  • Silver. Threat coverage of at least 90 percent, mutation kill of at least 65 percent, and at least 4 crates with Kani trust-boundary harnesses.
  • Gold. Threat coverage of 100 percent, mutation kill of at least 80 percent, and at least 8 crates with Kani trust-boundary harnesses.

These tiers describe a peer's published assurance evidence. Runtime Assurance Tiers are a separate ladder that a cluster operator assigns to its own nodes, and the two ladders are not interchangeable.

Read more in Federation Assurance, which covers the evidence behind each tier.

The prepared example pins one peer and refuses three handshakes

Establish a pinned peer relationship

Create two operator identities, verify the expected peer, and consume a receiver-owned challenge before using the relationship.

Complete the project setup, then run the program from federation-guide/. The download includes every constructor and input.

Terminalbash
cargo run --locked --bin handshake
Output on darwin/arm64 · exit 0
pinned peer: accepted; challenge consumed
same challenge: refused by receiving host
same identity with another key: refused
expired handshake: refused

The transport or host owns atomic challenge consumption and persistence. This local process demonstrates that boundary with an in-memory set; it does not provide a shared replay database.

Read the complete program 39 lines · complete file
src/bin/handshake.rsrust
use chio_core_types::Keypair;
use chio_federation::trust_establishment::KernelTrustExchange;
use std::{
    collections::HashSet,
    time::{SystemTime, UNIX_EPOCH},
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
    let alice_key = Keypair::generate();
    let bob_key = Keypair::generate();
    // Exchange these public keys through an authenticated operator channel.
    let alice = KernelTrustExchange::new("operator.alice", alice_key.clone())
        .with_trusted_peer("operator.bob", bob_key.public_key());
    let bob = KernelTrustExchange::new("operator.bob", bob_key)
        .with_trusted_peer("operator.alice", alice_key.public_key());
    // A fresh challenge is owned by the receiving host, not chosen by the peer.
    let challenge = Keypair::generate().public_key().to_hex();
    let mut outstanding = HashSet::from([challenge.clone()]);
    let envelope = alice.local_envelope("operator.bob", &challenge, now)?;
    assert!(outstanding.contains(&envelope.challenge.nonce));
    let peer = bob.accept_envelope(&envelope, "operator.alice", now)?;
    assert!(peer.is_fresh(now));
    assert!(outstanding.remove(&envelope.challenge.nonce));
    println!("pinned peer: accepted; challenge consumed");
    // The exchange validates signatures; this host supplies the replay boundary.
    assert!(!outstanding.remove(&envelope.challenge.nonce));
    println!("same challenge: refused by receiving host");
    let impostor = KernelTrustExchange::new("operator.alice", Keypair::generate());
    let forged = impostor.local_envelope("operator.bob", "new-challenge", now)?;
    assert!(bob.accept_envelope(&forged, "operator.alice", now).is_err());
    println!("same identity with another key: refused");
    assert!(bob
        .accept_envelope(&envelope, "operator.alice", now + 301)
        .is_err());
    println!("expired handshake: refused");
    std::fs::write("handshake.json", serde_json::to_vec_pretty(&envelope)?)?;
    Ok(())
}

Open the source file. Each program asserts its expected refusals; keys, timestamps, and digests change between runs.

You should see four lines. The first pins the peer and consumes its challenge. The next three are refusals: the same challenge presented again, the same kernel id under another key, and a handshake past its freshness window.

Every handshake error fails closed and leaves the peer unpinned

A PeerHandshakeError names the step that failed. PeerNotPinned and PeerStale come later, when the relationship is used: the first before any handshake, the second after the rotation deadline.

VariantConditionRepair
UnsupportedSchemaThe envelope names a schema this build rejectsAgree a schema version with the peer
CanonicalJsonThe envelope did not canonicalizeRe-sign from the canonical body
SigningFailedThe local key could not signCheck the local seed and key custody
InvalidSignatureThe signature fails under the declared keyRe-establish the expected key over an authenticated channel
CapabilityNegotiationNo protocol capability set both sides acceptEnable a shared capability set
AddressMismatchThe envelope is addressed to another kernelSend the challenge to the intended peer
KernelIdMismatchThe declared kernel id is not the expected onePin the peer you meant
ClockSkewExceededThe timestamp is outside the accepted skewSynchronize clocks, then retry
PeerNotPinnedThe peer has no pinned entry on this sideComplete the handshake before use
PeerStaleThe pin passed its rotation deadlineRe-handshake the peer
MissingTrustAnchorNo trust anchor exists for first contactAdd the partner's card first
UnexpectedPeerKeyThe peer declared a key other than the pinned oneRe-establish the key over an authenticated channel
ConformanceTierBelowMinimumThe derived tier is below the quorum policy floorLower the floor deliberately, or wait for evidence
InvalidConformanceEvidenceThe conformance evidence fails validationHave the peer republish valid evidence
InvalidLadderManifestRefThe ladder manifest reference is malformed or expiredHave the peer sign and reference a current manifest
StorePoisonedThe local trust store is poisonedRestore the store from its durable backing

Treat UnexpectedPeerKey as a question for the partner's operator, never as a key to adopt.

Read more in Federation Refusal Codes, which indexes every federation code.

The transport or host consumes each challenge exactly once

The nonce makes every envelope unique, and the signature binds it. Rejecting a re-presented envelope inside the freshness window belongs to the layer in front of the exchange: mutual TLS on the default HTTPS transport, or a replay store that the receiving host keeps across worker restarts. The rotation deadline caps how long any pin stays usable.

A host embedding the exchange supplies a durable FederationPeerStore; the default InMemoryPeerStore lasts for one process, as does the example's set of consumed nonces.

Source

KernelTrustExchange, FederationPeer, ConformanceTier, ConformanceEvidence, and PeerHandshakeError live in crates/trust/chio-federation/src/trust_establishment.rs in the Chio repository, with the defaults DEFAULT_ROTATION_WINDOW_SECS and DEFAULT_HANDSHAKE_MAX_SKEW_SECS.

Next: agree what crosses the relationship