Operator Identity & Key Bindings
Tell a partner's kernel id, passport key, and transport endpoint apart, and see which check binds each one at your door.
On this page
A partner's kernel reaches yours under three identities, and your kernel binds each one with a different check. Knowing which check holds which identity tells you exactly where a forged or misrouted message stops.
The partner card is where the bindings start. It carries the partner's org DID, trust-control URL, authority key history, receipt-signer keys, and status resolve URL, and chio partner add pins the partner's kernel key from it. The three checks that bind a partner's identities all answer to that pin.
The handshake pins the passport key to the kernel id
The kernel id is an opaque string, did:chio:<hex> by convention. The handshake checks the declared kernel id first, then pins the declared passport key to it as a FederationPeer. The key is named by keyid = sha256(passport public key). Peer handshake.
One partner presents a kernel id, a passport key, and an endpoint
Each identity names the partner at a different layer, and each is pinned by a different record on your side.
- Kernel id. An opaque string,
did:chio:<hex>by convention. The peer handshake pins a declared key to it and keeps the pin as aFederationPeer. - Passport signing key. The partner's long-term operator key, named by
keyid = sha256(passport public key). Adding the card pins it; the handshake and the verifier's peer pins check every signature against it. - Transport endpoint id. The iroh Ed25519 endpoint key. The transport directory entry binds it to the kernel id, endorsed by the passport key.
The handshake checks the declared kernel id first, then the key. The directory binding matters only on the optional iroh lane; the default HTTPS transport authenticates each crossing with mutual TLS or a signature over the body.
Read more in Peer Pins & Trust Bundles, which covers how pins persist and rotate.
A did:chio identifier is its own public key
A did:chio identity uses an Ed25519 keypair, and the method encodes the public key directly into the identifier. Resolution is a local derivation, with no registry lookup and no network call.
did:chio:{64-hex-character-ed25519-public-key}
Example:
did:chio:bff663535a5cf58658cc38595d20dc4501f6dfc076ddf07e20571d413a3ceb5aBecause the method-specific identifier is the key, a party that knows the DID verifies signatures with it without contacting another service. Federating two operators needs no shared identity provider. chio did resolve produces the document from the key alone:
$ chio did resolve \
--public-key 3cdb65ef41fc1f109bcedbd8cfbd792e0851c6f2acbe5008576400bf91d04d09 \
--receipt-log-url https://trust.org-a.example/v1/receipts{
"@context": "https://www.w3.org/ns/did/v1",
"id": "did:chio:3cdb65ef41fc1f109bcedbd8cfbd792e0851c6f2acbe5008576400bf91d04d09",
"verificationMethod": [
{
"id": "did:chio:3cdb65ef41fc1f109bcedbd8cfbd792e0851c6f2acbe5008576400bf91d04d09#key-1",
"type": "Ed25519VerificationKey2020",
"controller": "did:chio:3cdb65ef41fc1f109bcedbd8cfbd792e0851c6f2acbe5008576400bf91d04d09",
"publicKeyMultibase": "z6MkiYpYWyUNXHzHZsgmMPy4xypRKqdJjgnmAmNMtN2DDTBW"
}
],
"authentication": [
"did:chio:3cdb65ef41fc1f109bcedbd8cfbd792e0851c6f2acbe5008576400bf91d04d09#key-1"
],
"assertionMethod": [
"did:chio:3cdb65ef41fc1f109bcedbd8cfbd792e0851c6f2acbe5008576400bf91d04d09#key-1"
],
"service": [
{
"id": "did:chio:3cdb65ef41fc1f109bcedbd8cfbd792e0851c6f2acbe5008576400bf91d04d09#receipt-log",
"type": "ChioReceiptLogService",
"serviceEndpoint": "https://trust.org-a.example/v1/receipts"
}
]
}The resolved document is deterministic:
@contextpoints at the W3C DID v1 context.- One
verificationMethodentry,#key-1, of typeEd25519VerificationKey2020, with itspublicKeyMultibasein base58btc. authenticationandassertionMethodboth reference#key-1.- Optional
ChioReceiptLogServiceentries point at the operator's receipt query endpoint, so a counterparty can pull downstream evidence.
Passing --did with the fully qualified identifier in place of --public-key produces the same document. Omitting --receipt-log-url omits the service array and leaves the rest unchanged.
The passport key anchors every binding the partner signs
Resolving a DID tells you which public key belongs to an identifier. Whether your kernel recognizes that key for the relationship is your trust store's decision, made when you add the partner's card. From then on, the handshake checks the partner against the pinned key, and the transport directory endorses each endpoint under it. Every verifier on your side reads the same pin, and chio trust pins shows where it came from:
chio trust pins --role receipt-kernelname role key source partner receipt-kernel ed25519:9b07…d31c partner card, did:chio:z6MkpA7…c2T file ~/.chio/trust.toml
A key that never arrived on a card has no row here, however valid its DID document, and the handshake refuses it with MissingTrustAnchor or UnexpectedPeerKey.
A projected Agent Passport credential can bind to did:web, did:key, or did:jwk for portability. did:chio stays the provenance anchor in every projection, carried in chio_subject_did and chio_issuer_dids, so a verifier can trace any projection back to the key it pinned.
Read more in Agent Passports, which covers the credential model, its projections, claim families, and disclosure rules.
Each iroh lane checks the binding its frames depend on
On the optional iroh lane, the authenticated QUIC connection supplies an endpoint id. The three lanes turn that endpoint into a trusted sender in three ways:
- Signals. The receiver resolves the authenticated endpoint id to a kernel id through the signed directory. The per-frame verifier then requires every frame's kernel id to match that sender, and each deposit verifies under its agent's passport key. A deposit signed with a kernel key is refused as
KernelKeyUsedForDeposit. - Revocation. The wire types carry only a
signer_id. The receiver projects that signer's key from the issuer-signed transport directory, requires the signer to be pinned to the authenticated endpoint, and verifies every root against it. - Co-sign. The accepting kernel binds the authenticated endpoint id to the kernel id the requester claims before it co-signs. A request that claims a different kernel id is refused, and no signature is produced.
Signals is the public name for the pheromone layer; the lane keeps the pheromone name in its ALPN. An endpoint the directory does not bind to an admitted kernel id never reaches any of the three.
Sources for the lane bindings
The "Per-Lane Identity Bindings Differ" section of docs/adr/ADR-0014-iroh-federation-transport.md, and crates/trust/chio-federation-transport-iroh/src/lanes/bilateral.rs in the Chio repository.
Read more in Transport Lanes, which documents each lane's frames and verifier.
A broken binding names the check that refused it
Each binding fails with its own code, so the refusal tells you which identity disagreed with your pins.
KernelIdMismatchcomes from the handshake when the declared kernel id is not the one the caller expected. It is checked before any pin lookup.UnexpectedPeerKeycomes from the handshake when the declared key differs from the pin, andMissingTrustAnchorwhen no pin or trust anchor exists for the peer at all.PeerUnpinnedOrKeyidMismatchcomes from the bilateral verifier when a predicate's kernel id is unpinned, when its declared passport key fingerprint differs from the pinned key's, or when the resolved receipt's kernel key differs from the pinnedtool_server_bkey.- QUIC application error code 403 with reason
not admittedcomes from the transport gate when the endpoint is not bound to an admitted kernel id, before any lane handler runs.
Read more in Federation Refusal Codes, which indexes every code.