Wire Protocol
Find the frame format, message catalog, hosted MCP session rules, and trust control plane endpoints an implementation must match.
On this page
A Chio frame is a 4-byte big-endian length prefix followed by one canonical JSON object, and that object's type, status, or code field names the message.
Agents exchange frames with the kernel directly. MCP clients reach the kernel through the hosted edge that chio mcp serve-http serves, and operators issue and revoke capabilities through chio trust serve. The keywords MUST, SHOULD, and MAY below are normative.
Try tool_call_chunk, MCP-Session-Id, or capabilityId. Each entry has its own link.
Synopsis
Wire version chio-wire-v1: a frame is a length prefix and one canonical JSON object whose type, status, or code field selects the variant. The smallest valid frame is an agent heartbeat: 20 payload bytes behind the prefix 00 00 00 14.
+----------------------+---------------------------+
| 4-byte length prefix | canonical JSON payload |
+----------------------+---------------------------+{"type":"heartbeat"}| Object | Discriminator | Variants |
|---|---|---|
AgentMessage | type | tool_, list_, heartbeat |
KernelMessage | type | tool_call_chunk, tool_, capability_list, capability_, heartbeat |
ToolCallResult | status | ok, stream_complete, cancelled, incomplete, err |
ToolCallError | code | capability_, capability_, capability_, policy_denied, tool_, internal_error |
Transports
Four cooperating transports carry Chio messages:
| Transport | Encoding | Carries |
|---|---|---|
Native Chio transport | Length-prefixed canonical JSON | Direct agent-to-kernel messaging |
Hosted MCP edge | JSON-RPC over HTTP POST plus SSE | Remote session admission and MCP-compatible runtime traffic |
Trust control plane | JSON over HTTP | Capability issuance, delegated issuance, receipt lookup, and revocation |
HTTP sidecar | JSON over HTTP (localhost sidecar) | Evaluation and receipt signing for arbitrary HTTP API requests |
Session initialization happens on the hosted MCP edge. The native transport has no initialization message: direct peers obtain capability material out of band and begin sending AgentMessage frames immediately.
Native framing
ℓlengthκcapabilitygguards
The tool’s output returns with the signed receipt
The kernel reads the 4-byte length prefix, decodes one canonical JSON object, and checks the capability and the guards before it invokes the tool. The terminal output returns as tool_call_response with status: ok and its value, carrying the signed ChioReceipt. Receipt format.
One governed call on the native transport is a tool_call_request frame in, zero or more tool_call_chunk frames out, and one tool_call_response that carries the result and the signed ChioReceipt. The kernel checks the capability and policy before it invokes the tool.
Framing
Each native Chio frame is a 4-byte length prefix followed by the canonical JSON payload.
- The length prefix MUST be an unsigned 32-bit integer in big-endian byte order.
- The length value MUST count only the payload bytes that follow the prefix.
- The payload MUST be canonical JSON as produced by RFC 8785-style canonicalization.
- The maximum permitted payload length is
16,777,216bytes (16 MiB). - A sender MUST NOT emit a larger frame.
- A receiver MUST reject any frame whose advertised length exceeds that maximum.
A payload length of 256 bytes is encoded as 00 00 01 00.
Sender requirements
- Senders MUST serialize native messages as canonical JSON before writing the frame.
- Senders MUST emit exactly one top-level JSON object per frame.
- Senders MUST use the discriminators and field names defined in the message catalog.
Receiver behavior and error recovery
The kernel's transport implementation defines native receiver behavior.
- If EOF occurs before the 4-byte prefix is fully read, the receiver MUST treat the connection as closed and deliver no partial message.
- If EOF occurs after the prefix but before the full payload is read, the receiver MUST treat the connection as closed and deliver no partial message.
- If the advertised length is greater than 16 MiB, the receiver MUST reject the frame as
message_too_large. - If the payload is not valid JSON for the expected message family, the receiver MUST reject the frame as a deserialization failure.
Recovery follows three rules:
- The native framed transport has no in-band resynchronization marker.
- After
connection_closed,message_too_large, or deserialization failure, an implementation SHOULD close the current transport and establish a new connection before continuing. - Serialization failure on send is terminal for the attempted message; no partial frame is defined.
Native versioning
- The native framed transport version is
chio-wire-v1. - Native direct peers do not negotiate in-band; compatibility is an exact-match, out-of-band requirement.
- Because no in-band downgrade exists, incompatible native peers MUST close or reset the transport instead of attempting best-effort interop.
Native message catalog
The core message types and the kernel transport define the catalog. Every frame is a JSON object with a type discriminator.
AgentMessage
Agent-to-kernel frames.
type | Required fields | Meaning |
|---|---|---|
tool_ | id, capability_, server_id, tool, params | Invoke one tool under one signed capability |
list_ | none | Request the caller's valid capabilities from the kernel |
heartbeat | none | Liveness probe |
tool_call_request required fields:
| Field | Type | Meaning |
|---|---|---|
id | string | Correlation identifier echoed by kernel responses |
capability_ | CapabilityToken object | Signed authority for this call |
server_id | string | Target tool server identifier |
tool | string | Tool name within the target server |
params | JSON value | Tool arguments |
tool_call_request optional fields, from the Rust type and the schema spec/schemas/chio-wire/v1/agent/tool_call_request.schema.json:
| Field | Type | Meaning |
|---|---|---|
governed_intent | Governed object | Governed transaction the agent asks the kernel to authorize |
approval_token | Governed object | Single approval covering the governed intent |
approval_tokens | array of Governed | Approvals collected for a threshold proposal |
threshold_ | Threshold object | Threshold policy the collected approvals are counted against |
supplemental_ | Opaque object | Opaque authorization extension carried through to the kernel |
execution_nonce | Signed object | Kernel-issued nonce from a strict execution preflight for this request |
The schema closes the object with "additionalProperties": false, so a peer sends no other field. AgentMessage::authorization_conflict names the combinations the native session handler refuses before evaluation: approval_token together with a non-empty approval_tokens; a threshold_approval_proposal with an empty approval_tokens; and a non-empty approval_tokens without a threshold_approval_proposal.
KernelMessage
Kernel-to-agent frames.
type | Required fields | Meaning |
|---|---|---|
tool_call_chunk | id, chunk_index, data | Streaming chunk emitted before the final response |
tool_ | id, result, receipt | Terminal result plus signed receipt |
capability_list | capabilities | Reply to list_ |
capability_ | id | Notification that a capability identifier is revoked |
heartbeat | none | Liveness reply |
tool_call_chunk fields:
| Field | Type | Meaning |
|---|---|---|
id | string | Parent request identifier |
chunk_index | integer | Zero-based arrival index |
data | JSON value | Stream payload |
tool_call_response fields:
| Field | Type | Meaning |
|---|---|---|
id | string | Parent request identifier |
result | ToolCallResult object | Terminal execution status |
receipt | ChioReceipt object | Signed receipt for the evaluated action |
execution_nonce | Signed object, optional | Kernel-issued nonce to present on the exact retry |
capability_list carries one field, capabilities: an array of CapabilityToken the kernel considers valid. capability_revoked carries one field, id: the revoked capability identifier, as a string.
ToolCallResult
KernelMessage.tool_call_response.result is a tagged object with a status discriminator.
status | Required fields | Meaning |
|---|---|---|
ok | value | Tool completed and returned a value |
stream_complete | total_chunks | Tool completed after streaming chunks |
cancelled | reason, chunks_received | Explicit cancellation |
incomplete | reason, chunks_received | Non-terminal interruption or upstream truncation |
err | error | Refusal or failure |
ToolCallError
ToolCallResult.status = "err" carries a tagged error object with a code discriminator.
code | Required fields | Meaning |
|---|---|---|
capability_ | detail string | Capability was malformed, invalid, or bound to the wrong subject |
capability_ | none | Capability is outside its validity window |
capability_ | none | Capability identifier is revoked |
policy_denied | detail.guard, detail.reason | A policy guard refused the action |
tool_ | detail string | Upstream tool server failed |
internal_error | detail string | Kernel-side internal failure |
Signed artifact requirements
Two nested signed Chio objects appear directly on the native wire: CapabilityToken and ChioReceipt.
- Implementations MUST preserve all fields on those nested objects exactly as received.
- A native sender MUST NOT rewrite or recanonicalize nested signed object fields prior to forwarding.
- Receivers SHOULD verify signatures before treating capability or receipt content as authoritative.
Hosted MCP HTTP session transport
Initialize opens the session and names it
The client posts initialize to POST /mcp without MCP-Session-Id. The edge spawns a session and answers on an SSE stream whose HTTP response carries the new MCP-Session-Id. The client sends notifications/initialized before any ready-state call such as tools/call, and may open one GET /mcp notification stream. Initialization rules.
chio mcp serve-http serves the hosted edge. JSON-RPC requests arrive over HTTP POST, and results and notifications return on SSE streams bound to one session.
Endpoints and session headers
POST /mcpcarries JSON-RPC requests and notifications.GET /mcpis the notification replay and live stream.DELETE /mcpterminates an existing session.
The session headers are MCP-Session-Id and MCP-Protocol-Version.
Initialization rules
- An
initializerequest MUST be sent toPOST /mcp. - An
initializerequest MUST be a JSON-RPC request and therefore include anid. - An
initializerequest MUST NOT includeMCP-Session-Id. - A successful initialize response MUST return an SSE stream and include
MCP-Session-Idon the HTTP response. - After successful initialize, the client MUST send
notifications/initializedbefore relying on ready-state operations.
Established-session rules
- Non-initialize requests MUST include
MCP-Session-Id. - If the session has a bound protocol version and the client sends
MCP-Protocol-Version, that header MUST match the stored session value. - POST requests MUST use
Content-Type: application/json. - GET notification streams MUST include
MCP-Session-Id. - Ready-state methods such as
tools/listandtools/callMUST not run until the session has receivednotifications/initialized.
The edge checks request headers before it reads a body. A POST must advertise both application/json and text/event-stream in Accept, and a GET must advertise text/event-stream; otherwise the edge answers 406 Not Acceptable. A POST whose Content-Type does not start with application/json gets 415 Unsupported Media Type. An MCP-Protocol-Version that differs from the session's bound version gets 400 Bad Request, and an unknown MCP-Session-Id gets 404 Not Found.
Notification stream and replay
- At most one active GET notification stream is allowed per session.
- Notification replay uses the
Last-Event-IDrequest header. - Event identifiers are encoded as
{session_id}-{sequence}. - Replay requests outside the retained event window fail with
409 Conflict. - Hosted deployments that reuse one upstream owner across multiple sessions MUST keep late notifications and task handles scoped to the originating session. They MUST NOT fan out unattributed notifications or allow a different session to act on a foreign task id.
Hosted session lifecycle
Hosted sessions move through six states: initializing, ready, draining, deleted, expired, and closed.
Requests against draining, deleted, expired, or closed sessions MUST not silently resume prior state. Clients encountering those terminal states MUST re-run initialization.
Version negotiation
spec/versions/chio-protocol-negotiation.v1.json is the machine-readable negotiation artifact.
- Clients MAY send
initialize.params.protocolVersion. - The server compares that value against its supported version set, which holds one MCP protocol version:
2025-11-25. - Compatibility is an exact-match test against that set. There is no downgrade path: the edge rejects an unsupported requested version.
- On success, the selected version is echoed in
result.protocolVersionand exposed again underresult.capabilities.experimental.chioProtocol.selectedProtocolVersion. - On failure, initialize is rejected with JSON-RPC
-32600plus a structured Chio protocol error descriptor inerror.data.chioError.
Model metadata admission
- Hosted
tools/callrequests MAY carry model metadata under either_meta.modelMetadataor_meta.chioModelMetadata. - The hosted edge normalizes that payload into Chio request
model_metadata. - Caller-supplied metadata MUST be treated as
assertedprovenance at admission time even if the caller marks itverified. - Receipts and downstream exports MUST preserve the effective provenance class instead of silently upgrading caller assertions into verified runtime truth.
Trust control plane endpoints
The authority signs a token for one subject and scope
The caller posts subjectPublicKey, scope, and ttlSeconds to POST /v1/capabilities/issue, with optional runtimeAttestation for issuance policy. The authority issues a CapabilityToken for that subject, scope, and lifetime, and the service returns it as capability. Capability issuance.
chio trust serve runs the Trust Control Plane. It issues capabilities directly and under federation, answers receipt queries, and records revocations, all as JSON over HTTP. The revocation types are RevokeCapabilityRequest and RevokeCapabilityResponse.
Capability issuance
Endpoint: POST /v1/capabilities/issue. The request body:
| Field | Type | Meaning |
|---|---|---|
subject | string | Ed25519 subject key in hex |
scope | ChioScope | Capability scope to issue |
ttlSeconds | integer | Requested lifetime |
runtime | object, optional | Attestation evidence used by issuance policy |
The success response carries one field, capability: the signed issued CapabilityToken.
Federated and delegated issuance
Endpoint: POST /v1/federation/capabilities/issue. Delegation-relevant request fields:
presentation: passport presentation proving federated subject identity.expectedChallenge: challenge the presentation must satisfy.capability: requested issued capability body.delegationPolicy: optional signed ceiling for delegated issuance.upstreamCapabilityId: optional imported parent capability anchor for multi-hop lineage.
Success response fields relevant to delegation:
capability: newly issued child capability.delegationAnchorCapabilityId: optional lineage anchor persisted by the trust control plane.subjectPublicKey: resolved subject key used for issuance.
The service verifies the presentation and challenge, checks the delegation policy, resolves upstream state, and persists applicable lineage before it answers. Its delegation rules:
- If
upstreamCapabilityIdis supplied, the request MUST also carry a delegation policy bound to that exact parent capability id. - If a delegation policy is supplied, the requested child capability MUST NOT exceed the policy ceiling.
- The trust control plane MUST reject untrusted delegation-policy signers.
Receipt query
Endpoint: GET /v1/receipts/query. Supported query parameters:
| Parameter | Type | Meaning |
|---|---|---|
capabilityId | string | Filter by capability identifier (exact match) |
toolServer | string | Filter by tool server name (exact match) |
toolName | string | Filter by tool name (exact match) |
outcome | string | Filter by decision outcome: allow, deny, cancelled, or incomplete |
since | integer | Receipts with timestamp at or after this Unix second |
until | integer | Receipts with timestamp at or before this Unix second |
minCost | integer, u64 minor units | Receipts whose charged cost is at least this amount |
maxCost | integer, u64 minor units | Receipts whose charged cost is at most this amount |
costCurrency | string | Currency for the cost bounds |
cursor | integer | Forward pagination: receipts with a sequence greater than the cursor |
limit | integer | Maximum receipts per page, capped by the service |
agentSubject | string | Filter by agent subject public key, hex-encoded Ed25519 |
minCost and maxCost are unsigned 64-bit minor-unit values. When either is set, receipts without financial metadata are excluded. Supplying either requires costCurrency as exactly three uppercase ASCII letters, and when both bounds are present, minCost must not exceed maxCost. ReceiptQuery::validated_cost_currency applies these rules.
| Response field | Type | Meaning |
|---|---|---|
totalCount | integer | Total matched receipts |
nextCursor | integer or null | Cursor for the next page |
receipts | array | Receipt rows serialized as JSON values |
Revocation
Endpoint: POST /v1/revocations. The request body carries one field, capabilityId: the capability identifier to revoke, as a string.
| Response field | Type | Meaning |
|---|---|---|
capabilityId | string | Revoked identifier |
revoked | boolean | Revocation state after the call |
newlyRevoked | boolean | Whether this call changed state |
Revocation changes the state the kernel consults on use. The agent learns of it in one of two ways: a capability_revoked notification that can arrive before its next call, or a capability_revoked error and a signed receipt on its next tool_call_request under that capability.
Error taxonomy
spec/errors/chio-error-registry.v1.json is the machine-readable error registry. It guarantees four properties for every entry:
- a unique numeric code
- exactly one named category
- a
transient: true|falsemarker - explicit retry guidance
Registry categories: protocol, auth, capability, guard, budget, tool, internal, transaction.
Each transport maps onto the registry:
- Native Chio
ToolCallErrordiscriminators map deterministically to registry entries such ascapability_denied,capability_expired,capability_revoked,policy_denied,tool_server_error, andinternal_error. - Hosted MCP initialize-time protocol rejection communicates the numeric Chio protocol code under JSON-RPC
error.data.chioError.code. - The trust control plane answers with HTTP status codes; registry codes define the stable cross-transport classification and retry semantics.
Native failure handling
| Failure | Response | Transport and evidence |
|---|---|---|
Prefix exceeds 16 MiB | message_ | Close or reset the transport. |
Partial prefix or payload | connection_ | Discard the partial message. |
Policy refuses a tool call | tool_ with policy_denied | Includes the decision receipt. |
HTTP sidecar
The HTTP sidecar lets the kernel evaluate and sign receipts for arbitrary HTTP API requests alongside agent-protocol traffic. spec/HTTP-SUBSTRATE.md defines the sidecar evaluation protocol, the typed request and receipt models, and the deterministic mapping from HttpReceipt to the core ChioReceipt type. HTTP Transport documents it in full.
Sidecar evaluation protocol
A Chio kernel runs as a local process and exposes these HTTP endpoints on localhost:
| Endpoint | Method | Purpose |
|---|---|---|
/chio/evaluate | POST | Evaluate an HTTP request against loaded policy |
/chio/verify | POST | Verify receipt integrity, signer trust, and recorded authority semantics |
/chio/health | GET | Sidecar health check |
Language-specific sidecar middleware (chio-spring-boot for the JVM, ChioMiddleware for .NET, and the Python, TypeScript, and Go sidecar SDKs) intercepts incoming HTTP requests, constructs a ChioHttpRequest, sends it to POST /chio/evaluate, and enforces the returned verdict.
Relationship to language SDKs
Sidecar SDKs are HTTP clients of the sidecar evaluation protocol and hold no policy of their own. The kernel is the single trust anchor for capability validation, guard evaluation, and receipt signing. Rust's chio-tower evaluates through the kernel inside the application process; see the Rust integration contract.
Schemas and conformance
Versioned native message schemas live under spec/schemas/chio-wire/v1/, one directory per message family. The validation test loads these files:
| Family | Directory | Schema files the test exercises |
|---|---|---|
AgentMessage | agent/ | tool_, list_, heartbeat |
KernelMessage | kernel/ | tool_call_chunk, tool_, capability_list, capability_, heartbeat |
ToolCallResult | result/ | ok, stream_complete, cancelled, incomplete, err |
ToolCallError | error/ | capability_, capability_, capability_, policy_denied, tool_, internal_error |
- Schema files in that directory are the machine-readable contract for the native Chio message families.
- Implementations MUST continue to serialize the native message variants in a form accepted by those schemas.
- Schema validation MUST be exercised against live Rust serialization, not handwritten examples alone.
The validation test serializes live Rust values of each variant and validates them against the schema files named in the table.
The signed-artifact registry, spec/schemas/registry.json, registers schema ids under the same tree for the signed objects the wire carries. The message-family directories sit outside it. Each registered directory and its ids:
| Directory | Registered | Schema ids |
|---|---|---|
v1/ | 13 | chio., chio., chio., chio., chio., chio., chio., chio., chio., chio., chio.receipt.v1, chio., chio. |
| File | Title | Registry ID | Version |
|---|---|---|---|
chio-wire/ | Chio Governed Active Response Intent Body | unregistered | v1 |
chio-wire/ | Chio Governed Transaction Intent | unregistered | v1 |
chio-wire/ | Chio AgentMessage heartbeat | unregistered | v1 |
chio-wire/ | Chio AgentMessage list_ | unregistered | v1 |
chio-wire/ | Chio AgentMessage tool_ | unregistered | v1 |
chio-wire/ | Chio anchor batch v1 | chio. | v1 |
chio-wire/ | Chio Aggregate Budget Root Binding | chio. | v1 |
chio-wire/ | Chio Aggregate Invocation Budget | unregistered | v1 |
chio-wire/ | Chio capability negotiation v1 | chio. | v1 |
chio-wire/ | Chio Cumulative Approval Root Binding | chio. | v1 |
chio-wire/ | Chio Governed Approval Token | unregistered | v1 |
chio-wire/ | Chio Capability Grant | unregistered | v1 |
chio-wire/ | Chio Capability Revocation Entry | unregistered | v1 |
chio-wire/ | Chio Opaque Supplemental Authorization | unregistered | v1 |
chio-wire/ | Chio Threshold Approval Proposal | chio. | v1 |
chio-wire/ | Chio CapabilityToken | chio. | v1 |
chio-wire/ | Chio Verified Approval Set Body | unregistered | v1 |
chio-wire/ | Chio ToolCallError capability_ | unregistered | v1 |
chio-wire/ | Chio ToolCallError capability_ | unregistered | v1 |
chio-wire/ | Chio ToolCallError capability_ | unregistered | v1 |
chio-wire/ | Chio ToolCallError internal_error | unregistered | v1 |
chio-wire/ | Chio ToolCallError policy_denied | unregistered | v1 |
chio-wire/ | Chio ToolCallError tool_ | unregistered | v1 |
chio-wire/ | Chio bilateral DSSE signature-slice envelope | chio. | v1 |
chio-wire/ | Chio bilateral DSSE signature-slice statement | unregistered | v1 |
chio-wire/ | Chio JSON-RPC 2.0 Notification | unregistered | v1 |
chio-wire/ | Chio JSON-RPC 2.0 Request | unregistered | v1 |
chio-wire/ | Chio JSON-RPC 2.0 Response | unregistered | v1 |
chio-wire/ | Chio KernelMessage capability_list | unregistered | v1 |
chio-wire/ | Chio KernelMessage capability_ | unregistered | v1 |
chio-wire/ | Chio Combined Admission Capture Metadata | unregistered | v1 |
chio-wire/ | Chio signed execution nonce | unregistered | v1 |
chio-wire/ | Chio KernelMessage heartbeat | unregistered | v1 |
chio-wire/ | Chio KernelMessage tool_call_chunk | unregistered | v1 |
chio-wire/ | Chio KernelMessage tool_ | unregistered | v1 |
chio-wire/ | Chio Provenance Attestation Bundle | unregistered | v1 |
chio-wire/ | Chio Provenance Call-Chain Context | unregistered | v1 |
chio-wire/ | Chio Provenance Stamp | unregistered | v1 |
chio-wire/ | Chio Provenance Verdict Link | unregistered | v1 |
chio-wire/ | Chio Durable Admission Receipt Metadata | chio. | v1 |
Source
Specification and implementation sources
The lookup reflects spec/WIRE_PROTOCOL.md in the chio repository. Status: normative. Version 1.0, dated 2026-04-13.
Where the specification defers to code, the lookup reflects these sources:
- Exchange sequences in the specification: hosted initialization, lines 456 to 471; capability issuance, 475 to 485; delegated issuance, 489 to 508; native invocation, 512 to 528; revocation, 532 to 550.
- Native message catalog:
crates/core/chio-core-types/src/message.rs. - Native framing and receiver behavior:
crates/kernel/chio-kernel/src/transport.rs. - Hosted edge:
crates/protocol/chio-mcp-remote/src/remote_mcp/http_service.rs; header checks incrates/protocol/chio-mcp-remote/src/remote_mcp/oauth/bearer_auth.rs. - Trust control plane routes:
crates/platform/chio-control-plane/src/trust_control/service_types/paths.rs; issuance, federated issuance, and receipt query types incrates/platform/chio-control-plane/src/trust_control/service_types/requests.rs; revocation types incrates/platform/chio-control-plane/src/trust_control/config_and_public.rs. - Receipt query cost validation:
crates/kernel/chio-kernel/src/receipt_query.rs. - Sidecar routes:
crates/products/chio-api-protect/src/proxy/router.rs; platform-level SDK documentation indocs/sdk/PLATFORM.md. - Schema validation test:
crates/core/chio-core-types/tests/wire_protocol_schema.rs.