LearnSystem Architecture
How a Call Enters the Kernel
The kernel joins a call path in four shapes, and in every one the adapter that receives the traffic is untrusted.
Overview
Adopting Chio does not mean routing traffic to a new address. The kernel produces a verdict for a ToolCallRequest, and an adapter puts that verdict on the path a request was already taking. Two of the four shapes make the verdict an ordinary function call inside the process that was already handling the request. A third makes the kernel the server the client was already going to call. Only the fourth sends anything over the network, and what it sends is the question rather than the traffic.
The adapters live in the workspace's protocol directory, which holds 28 crates. Each one owns a translation and a transport. None of them owns a decision.
Cargo.tomlat fe56570Model
Three crates supply four shapes. Each shape differs in what receives the bytes and in whether anything leaves the receiving process. The kernel behind them is the same in all four.
| Shape | Entry type | What crosses a process boundary |
|---|---|---|
Middleware in an HTTP service you already run, chio-tower | ChioLayer / ChioService | Nothing. ChioService terminates the inbound HTTP request and delegates the decision to chio_http_core::HttpAuthority, which is backed by chio_kernel. |
Direct dispatch from your own code, chio-tower | KernelService / build_layered | Nothing, and there is no HTTP shape at all. KernelService::call dispatches straight to ChioKernel::evaluate_tool_call. |
The protocol server the client already speaks to, chio-mcp-edge | ChioMcpEdge | The MCP session. The client speaks JSON-RPC 2.0 to the edge, which owns the method set and the task lifecycle and dispatches every tool call, resource read, prompt fetch, and completion through chio_kernel::ChioKernel. |
An authorization service your proxy consults, chio-envoy-ext-authz | ChioExtAuthzService | One CheckRequest over gRPC. Envoy keeps the data path; the crate speaks the ext_authz contract on one side and a ToolCallRequest / Verdict pair on the other. |
Whether to route traffic through Chio or to make Chio part of what already runs has one answer in three of these four rows: the request stays where it was. The Envoy row is the exception, and what the adapter emits into a ToolCallRequest is a translated description rather than the traffic: the body reaches the kernel as a SHA-256 digest.
How it works
What every shape does identically
Each adapter performs the same three steps in the same order. It projects whatever it received into a ToolCallRequest, hands that request to the kernel, and maps the returned Verdict back into its own protocol. Capability validation, guard evaluation, budget admission, and receipt signing all happen on the kernel side of that call. The Envoy adapter derives an http.<method>.<path> tool identity and extracts caller identity in a fixed order: x-chio-capability-token, then Authorization: Bearer, then the mTLS peer principal, then anonymous. The MCP edge validates every tool manifest through chio_manifest::validate_manifest before it exposes a single tool. The Tower service buffers and hashes the body, builds the evaluation input, and calls ChioEvaluator::prepare. Different protocols, one shape of question.
The adapter holds no authority
An adapter sits on the attacker-facing side of the boundary and the crates say so. The Envoy adapter is described in its own architecture notes as an untrusted edge adapter, and it enforces that by depending on no chio-* crate at all. What it holds instead is a one-method trait that a caller implements.
/// Kernel abstraction used by [`ChioExtAuthzService`]. Real deployments supply
/// an implementation that delegates to `chio-kernel` (or `HttpAuthority` in
/// `chio-http-core`); tests can stub this trait to verify the adapter's
/// request/response plumbing in isolation.
#[async_trait]
pub trait EnvoyKernel: Send + Sync + 'static {
/// Evaluate a translated tool call. Implementations must be fail-closed:
/// return [`KernelError`] rather than panicking on internal faults so the
/// adapter can deny with a 500 response.
async fn evaluate(&self, request: ToolCallRequest) -> Result<Verdict, KernelError>;
}The MCP edge states the same property for JSON-RPC input, which is attacker-controlled: malformed or unauthorized input fails closed to a JSON-RPC error before it reaches kernel state. A params gate runs before dispatch, so non-object params on a known request method fail with -32602 before session, discovery, or kernel state is touched. The Tower crate terminates HTTP but delegates the decision, and describes the trust logic it does own as edge-only.
What the adapter is allowed to forward
Translation is lossy on purpose. The Envoy adapter excludes authorization and x-chio-capability-token from the forwarded header map and retains only a SHA-256 hex digest of the bearer token, and of the body when one is present. Header values written back onto a response are sanitized: control characters become spaces and an empty result becomes "unspecified", so a guard name or a denial reason cannot inject control bytes into a response header. A shape that cannot express a field does not get to invent one.
Guarantees and limits
Status: shipped. All four shapes are workspace crates under the protocol directory, and each carries its own architecture notes, invariants, and failure modes. The guarantees below are the ones those notes state; the limits are theirs as well.
- The decision is not the adapter's to make.
chio-envoy-ext-authzdepends on no internal Chio crate, so the code that translates Envoy traffic cannot reach kernel state even by accident.EnvoyKernelis the trait a caller implements to plug inchio-kernel,HttpAuthority, or another policy engine. - A fault denies. In the Envoy adapter, any translation error or
EnvoyKernel::evaluateerror produces aCode::Internaldenied response with a fixed, generic reason; the actual fault is logged and never returned to the caller. The Tower stack is fail-closed by default. - Fail-open is a configuration, and it is counted. The Tower evaluator can be built with
with_fail_open, which forwards a request unenforced whenprepareerrors. Every such bypass incrementschio_fail_open_suspected_total{surface="tower"}, and the counter is seeded to zero onChioService::newso an alert on a deployment that has never failed open does not fire. - An unauthorized MCP call is a tool failure, not a protocol fault. An unauthorized
tools/callreturns a normal JSON-RPC result carrying an MCP tool result withisError: true, so the calling model reads a failed tool rather than a broken transport. - A shape bounds what a policy can name. The Envoy adapter derives tool identity from method and path, and keeps only digests of the bearer token and body, so a guard reading those shapes decides over digests and an allowlisted header map. Protocol coverage per adapter is the fidelity question, and Protocol Bridges carries the matrix.
Next steps
- The Mediated Call · the ordered sequence that runs once a request reaches the kernel
- The State a Verdict Depends On · what the kernel reads, and who fetched it
- Listeners & Edges · operating an edge once you have chosen a shape
- Remote MCP Edge · sessions, tasks, and nested flows on the MCP shape
- Protocol Bridges · the bridge model and the per-protocol fidelity matrix