Chio/Docs
LOGIN · JOIN

PlatformProcess Model

Node

Listeners & Edges

Which wire protocols a node terminates, what each projection is allowed to publish, and the one receipt-write counter every edge keeps.

Projection here, hosting next door

Remote MCP Edge owns one shipped listener end to end: how chio mcp serve-http boots, how a request is admitted, how a token is bound to a sender, and what runs per session. This page owns the layer underneath all of that: the crates that turn one kernel decision into an MCP, A2A, or ACP response, what each of them refuses to advertise, and the counter all three write. Read Node Overview first for the process the edges live in.

An edge is a projection, not an authority

Three crates under crates/protocol/ terminate a wire protocol and expose Chio’s own tools through it: chio-mcp-edge, chio-a2a-edge, and chio-acp-edge. Each one takes a set of validated chio_manifest::ToolManifests, projects them into that protocol’s discovery shape, parses that protocol’s JSON-RPC envelope, and dispatches every governed call into chio_kernel::ChioKernel. Capability matching, guard evaluation, and receipt signing happen in the kernel. The edge decides what the answer looks like on the wire and nothing else.

Two more crates sit beside them. chio-cross-protocol holds the one orchestrator the A2A and ACP authoritative paths both run through: capability lineage, scope attenuation, route planning, and trace construction, so neither bridged edge reimplements them against the kernel on its own. Its own architecture note is explicit that it is a library and not an edge, with no transport and no wire format of its own. chio-edge-metrics holds the receipt-write counter type, and only those same three edges depend on it.

Read the shipped-listener line carefully, because it is narrower than the crate list suggests. Two commands terminate MCP inside a shipped binary: chio mcp serve, which wraps an upstream MCP server and runs ChioMcpEdge::serve_stdio over stdin and stdout, and chio mcp serve-http, the remote edge. A2A and ACP ship no listener. Both crates build JSON-RPC response values and, for A2A, Agent Card JSON, and leave the transport to the caller; chio-a2a-edge’s architecture note says outright that the crate has no HTTP server dependency. Their only in-repo consumers are chio-conformance, the fuzz workspace, and the two worked examples, which drive handle_jsonrpc from a stdio line loop.


One decision, three envelopes

The three edges differ in method names, discovery document, and task vocabulary. They agree on everything that matters: manifests in, kernel-signed receipt metadata out, one chio_receipt_write_total increment per terminal outcome.

EdgeDiscoveryBlocking callDeferred lifecycleShipped listener
chio-mcp-edgetools/list over McpExposedTooltools/calltasks/list, tasks/get, tasks/result, tasks/cancelchio mcp serve (stdio), chio mcp serve-http
chio-a2a-edgeAgentCard with an A2aSkillEntry per published skillmessage/sendmessage/stream, then task/get or task/cancelNone. The caller serves the JSON-RPC values.
chio-acp-edgesession/list_capabilities over AcpCapabilitytool/invoketool/stream, then tool/resume or tool/cancelNone. The caller serves the JSON-RPC values.

Neither deferred lifecycle streams. On A2A, message/stream allocates a task id and stores a Working DeferredA2aTask without calling the kernel at all; task/get executes the stored request once through the orchestrator, writes the terminal TaskResponse back into the table, and returns that same stored value on every later poll. ACP is the same shape with tool/resume in place of task/get. Both tables are bounded at 1024 tasks still in a non-terminal status and prune anything older than 5 minutes (MAX_DEFERRED_A2A_TASKS / DEFERRED_A2A_TASK_TTL_MILLIS, and the ACP pair of the same shape), with the sweep running before every stream, poll, and cancel. Terminal tasks are exempt from the cap and are bounded only by the TTL sweep. Both edges check owner_agent_id against the calling agent_id before serving or mutating a task.

That shape is published, not implied. runtime_lifecycle_contract returns a fixed record per lifecycle case, and the edges embed it in their response metadata under runtimeLifecycle so a client can read the delivery mode rather than guess it.

crates/protocol/chio-cross-protocol/src/lifecycle.rsrust
RuntimeLifecycleSurface::AcpAuthoritative => RuntimeLifecycleContract {
    surface: "acp_authoritative".to_string(),
    blocking_entrypoint: "tool/invoke".to_string(),
    stream_entrypoint: "tool/stream".to_string(),
    follow_up_entrypoint: "tool/resume".to_string(),
    cancel_entrypoint: "tool/cancel".to_string(),
    stream_delivery: "resumed_terminal_payload".to_string(),
    partial_output_delivery: "resumed_terminal_payload".to_string(),
    claim_eligible: true,
    compatibility_only: false,
},

There are four cases, not two: A2aAuthoritative, A2aCompatibility, AcpAuthoritative, and AcpCompatibility. The two compatibility records report claim_eligible: false, compatibility_only: true, and "unsupported" for the stream, follow-up, and cancel entrypoints. Branch on claimEligible, not on the method name: the compatibility record reuses the same blocking entrypoint string the authoritative one does.


What an edge refuses to publish

A tool that cannot be projected honestly onto a protocol is withheld from that protocol’s discovery document rather than advertised with a caveat nobody reads. BridgeFidelity is the three-way verdict, and published_by_default() is !matches!(self, Self::Unsupported { .. }).

crates/protocol/chio-cross-protocol/src/semantic_hints.rs8-13rust
#[serde(rename_all = "snake_case", tag = "kind")]
pub enum BridgeFidelity {
    Lossless,
    Adapted { caveats: Vec<String> },
    Unsupported { reason: String },
}

The inputs come from the tool definition and from x-chio-* extensions on its input or output schema, read in that order by semantic_hints_for_tool. Five hints exist. Two of them fall back to the tool definition’s latency_hint rather than to a constant, and a third inherits whatever streams_output resolved to.

ExtensionFieldDefault when absent
x-chio-publishpublishtrue
x-chio-approval-requiredapproval_requiredfalse
x-chio-streamingstreams_outputTrue when latency_hint is Moderate or Slow
x-chio-cancellationsupports_cancellationTrue when latency_hint is Slow
x-chio-partial-outputpartial_outputWhatever streams_output resolved to

The sixth extension is the target protocol. x-chio-target-protocol resolves through parse_discovery_protocol to one of six families: native, http, mcp, a2a, acp, and open_ai (the parser also accepts openai, and it trims and lowercases first). Absent, it defaults to the registry’s default, which both the A2A and ACP edges set to Native. Both edges register exactly two executors, McpTargetExecutor from chio-mcp-edge and OpenAiTargetExecutor, so supports_target_protocol is true for exactly three of the six. A tool declaring http, a2a, or acp as its target resolves to Unsupported and is withheld from the A2A and ACP discovery documents; an unparseable value fails edge construction outright. The MCP edge reads no x-chio-target-protocol at all and lists the tool regardless.

The three edges gate differently

EdgeWithheld from discoveryName collision across manifests
chio-a2a-edgex-chio-publish=false, an unregistered target protocol, and any tool whose approval_required hint is set, because the edge cannot project an interactive approval prompt.Published under {server_id}::{tool_name} with a chio:collision-qualified tag; a second identical qualified id gets a #n ordinal suffix. Looking the bare name up returns an explicit ambiguity error listing the qualified ids.
chio-acp-edgex-chio-publish=false, an unregistered target protocol, everything inferred into AcpCategory::Browser, and any AcpCategory::Tool whose has_side_effects is set.Withheld entirely. Fidelity becomes Unsupported and any capability or binding already recorded for that name is removed. Re-declaring the identical server and tool pair is not a collision.
chio-mcp-edgeNothing. build_exposed_tool_bindings validates each manifest and each schema and publishes every remaining tool.A hard construction failure: ManifestError::DuplicateToolName.

The MCP edge does not consult fidelity at all

chio-mcp-edge imports neither BridgeFidelity nor semantic_hints_for_tool, and the string x-chio-publish does not appear anywhere in the crate. A tool marked unpublishable is still listed by tools/list. What the MCP projection does carry is a pair of annotations derived from has_side_effects(readOnlyHint and destructiveHint, always the inverse of each other) plus an execution block carrying taskSupport: "optional" and, when a latency hint exists, a suggestedLatency label. If you need a tool hidden from an MCP client, keep it out of the manifest you hand the edge.

A gated tool does not disappear. Both A2A and ACP keep the verdict queryable: bridge_fidelity(id) returns the recorded BridgeFidelity including Unsupported entries that never reached the discovery document, and the reason string names the cause. For a published-but-lossy tool, the caveat list on Adapted is the honest description of the gap: on A2A a stream-capable tool carries the caveat that chunks are collated into the terminal task payload rather than pushed as incremental events.


The one path an authoritative call takes

Both the A2A and ACP edges reach the kernel through the same function, CrossProtocolOrchestrator::execute, supplying only a CapabilityBridge implementation for their envelope shape. The MCP edge reaches the kernel directly on its own tools/call path and appears on the other side of the orchestrator as McpTargetExecutor, the registered executor for an mcp target.

rendering
The path one bridged call takes through CrossProtocolOrchestrator::execute, from envelope parse to the metadata every edge attaches. A route planner Deny leaves through sign_planned_deny_response rather than an error return.
sourcecrates/protocol/chio-cross-protocol/src/orchestrator.rs:137-341at fe56570

Three steps in that sequence are worth reading literally. Identity fields are validated before any lineage exists and are never trimmed: origin_request_id, kernel_request_id, target_server_id, target_tool_name, and agent_id must be non-empty, unpadded, and free of control characters, so signed lineage describes exactly what the caller submitted. A caller-supplied capability reference is cross-checked against the active capability’s id, its parent hash, and the bridge’s own source_protocol, and a mismatch on the last of those is rejected even when id and hash are correct. And the attenuation is enforced, not merely computed:

crates/protocol/chio-cross-protocol/src/orchestrator.rsrust
let attenuated_scope = attenuate_scope_for_tool(
    &request.capability.scope,
    &request.target_server_id,
    &request.target_tool_name,
);
if !attenuated_scope.is_subset_of(&request.capability.scope) {
    return Err(BridgeError::InvalidAttenuation(
        "attenuated scope must remain a strict subset of the parent capability".to_string(),
    ));
}

attenuate_scope_for_tool keeps only the grants whose server and tool match the concrete target, or match by *, rewrites the wildcard to the concrete pair, and drops resource and prompt grants entirely. What crosses a protocol hop is therefore narrower than the parent capability by construction, and the envelope that records it carries schema chio.cross-protocol-cap.v1.

Route planning runs before dispatch and returns one of three decisions. Select keeps the requested target, Attenuate falls back to a different available one, and Deny refuses. Preference, native fallback, and an outright ban on projected protocols come from a chioControlPlane object inside the governed intent’s context, read under both camelCase and snake_case keys. Two properties of the planner matter operationally: it never selects a non-native target with no registered executor, however the availability map is set, and with no candidate available it fails closed to Deny. A denied route is not an error return. The kernel signs a deny response through sign_planned_deny_response, the orchestrator still assembles route and trace evidence, and the caller gets a receipt-bearing refusal. Route selection evidence and any caller-supplied receipt_context are threaded into an executed kernel call, so they land inside the signed receipt metadata and not only in the return value. A planned deny carries the route evidence but not the caller’s receipt_context: metadata_with_source_receipt_context runs on an executed target and not on the deny branch. The signed route plan itself is covered in Route Plans.

What comes back is one OrchestratedToolCall, whose metadata() renders the chio object each edge attaches to its own protocol response: the full receipt and its id, a receiptRef carrying trace and bridge ids, the decision as allow, deny, or pending_approval, authorityPath fixed at cross_protocol_orchestrator, authoritative: true, the terminal state, the execution nonce, the capability envelope, and the route and trace records. trace_id and session_fingerprint are both SHA-256 over canonical JSON, the first over the request and route identity and the second over agent, capability, source protocol, and bridge id.

A multi-hop route still writes one receipt at the source edge

McpTargetExecutor::execute calls bridge_mcp_tool_call_from_response with its record_receipt_write argument set to false, so an ACP or A2A call routed to an MCP target advances the source edge’s counter and leaves the MCP edge’s counter alone. acp_edge_emits_chio_receipt_write_total and a2a_edge_emits_chio_receipt_write_total assert exactly that, with acp_before_target + 1 on one side and an unchanged MCP total on the other. Both edges also construct their McpTargetExecutor as a static with peer_supports_chio_tool_streaming: false, so a bridged MCP hop never negotiates chunk streaming.

The compatibility passthrough is not a receipt path

The A2A and ACP edges each carry a second, explicitly non-authoritative route that bypasses the kernel and calls a raw dyn ToolServerConnection directly. It exists for bounded migration and for tests. It is compiled only under cfg(test) or the compatibility-surface feature, which is not in either crate’s default feature set, and it is reachable only through a wrapper the caller has to name: ChioA2aEdgeCompatibility or ChioAcpEdgeCompatibility.

No kernel, no verdict, no receipt, no counter

A passthrough call evaluates no capability, runs no guard, signs nothing, and increments no chio_receipt_write_total counter in any of the four outcome buckets. It is not a deny and it is not an error; it is invisible to the receipt-write series entirely. A dashboard built on that counter cannot distinguish a quiet node from a node serving every call through the passthrough. If a build ships with compatibility-surface enabled, treat the absence of counter movement as unexplained rather than as evidence of idleness.

The responses label themselves. Every passthrough result on a given edge carries the same metadata block, with the authority fields hard-coded rather than derived from a verdict.

crates/protocol/chio-acp-edge/src/conversion.rsrust
#[cfg(any(test, feature = "compatibility-surface"))]
fn passthrough_metadata(reason: Option<&str>) -> Value {
    json!({
        "chio": {
            "receiptId": Value::Null,
            "receipt": Value::Null,
            "decision": "passthrough",
            "capabilityId": Value::Null,
            "authorityPath": "passthrough_compatibility",
            "authoritative": false,
            "compatibilityOnly": true,
            "claimEligible": false,
            "receiptBearing": false,
            "reason": reason,
        }
    })
}

The A2A block adds two keys to that shape: a runtimeLifecycle record built by runtime_lifecycle_metadata(RuntimeLifecycleSurface::A2aCompatibility) and a lifecycle object reporting messageSend: "blocking_terminal_task" and messageStream: "unsupported".

The discovery answer is labelled too. session/list_capabilities on the ACP passthrough attaches compatibility_surface_metadata(), which reports receiptBearingInvoke: false, the compatibility lifecycle record, and an explicit unsupportedLifecycleMethods array naming tool/stream, tool/cancel, and tool/resume. Calling any of those three on the passthrough returns JSON-RPC -32601 with a chio data block repeating authoritative: false.

The A2A passthrough also refuses message/stream with -32601, but it refuses more thinly: jsonrpc_stream_not_supported returns a bare message and no chio data block, so a client cannot read authoritative: false off that error. The permission analogue lives on ACP only. evaluate_permission_passthrough answers from configuration alone: allow when the published capability does not require permission, deny when it does or when the id is unknown, with no capability token consulted at all. A2A exposes no permission method for it to mirror.

The one behavior the passthrough does share with the kernel is its refusal to deadlock. Both edges cross from async into sync through block_on_tool_server_invoke, which mirrors the kernel’s own runtime-flavor gate: block_in_place on a multi-thread runtime, futures::executor::block_on with no runtime active, and a typed SyncBridgeIncompatibleWithCurrentThreadRuntime error under a current-thread runtime rather than parking the only worker thread. The ACP call site converts that error into a failed passthrough response carrying the same non-authoritative metadata.

The kernel-backed ACP preview is the case most easily confused with the passthrough, so read the difference. session/request_permission on the authoritative path runs evaluate_permission_with_kernel, which verifies the capability signature, its validity window, its subject against the calling agent, and the request against the matched grant, and consults verify_dpop_for_permission_preview when the grant requires sender binding. It still returns permission_preview_metadata with authoritative: false and previewOnly: true, because it deliberately does not consume the DPoP nonce a later tool/invoke will spend. The difference between the two previews is the invokeAuthorityPath field: cross_protocol_orchestrator for the kernel-backed one, passthrough_compatibility for the other.


One counter, four outcomes, three instances

Every edge writes the same series, chio_receipt_write_total, declared once in the workspace registry chio-metrics-spec as a counter with a single outcome label. chio-edge-metrics re-exports the registered name rather than redeclaring the string, and implements the counter behind it.

crates/protocol/chio-edge-metrics/src/lib.rs34-40rust
/// Stable exporter order for receipt-write outcome samples.
pub const RECEIPT_WRITE_OUTCOMES: [ReceiptWriteOutcome; 4] = [
    ReceiptWriteOutcome::Allow,
    ReceiptWriteOutcome::Deny,
    ReceiptWriteOutcome::PendingApproval,
    ReceiptWriteOutcome::Error,
];
LabelWritten whenCounts as a failure
allowVerdict::Allow reaches the edge sink.No
denyVerdict::Deny. A policy refusal is a successful receipt write.No
pending_approvalVerdict::PendingApproval. Normal human-in-the-loop flow.No
errorThe edge could not obtain a verdict at all, plus any unrecognized label passed to record.Yes

The error bucket is narrow on purpose, and the three edges narrow it slightly differently. The MCP edge routes kernel errors through record_receipt_write_kernel_error, which excludes exactly two variants: RequestCancelled and UrlElicitationsRequired, both of which are retryable or caller-driven rather than infrastructure faults. The A2A and ACP edges use record_receipt_write_bridge_error, which increments only for BridgeError::Kernel: an unregistered target protocol, a malformed envelope, or a failed attenuation returns an error to the caller and writes no counter at all.

Counter state is per edge, and that is the reason the crate exposes an instance type rather than module-level statics. Each edge declares its own static COUNTERS: ReceiptWriteCounters = ReceiptWriteCounters::new(); and delegates recording and rendering to it. Increments run through fetch_update with saturating_add, so a counter caps at u64::MAX instead of wrapping, which is pinned by receipt_write_counters_saturate_instead_of_wrapping. Both record and total map an unknown label to the error bucket rather than dropping it, so total("totally-unknown") returns the error count.

Rendering is deliberately minimal: one # HELP line, one # TYPE line, and four labelled samples in the fixed order above, so exposition text is byte-stable across scrapes. Each edge’s render_*_edge_metrics_prometheus then appends the receipt-writer liveness gauges, which is why a wedged writer shows up on the same scrape as the outcome totals.

render_acp_edge_metrics_prometheus(ReceiptWriterLiveness::Wedged)text
# HELP chio_receipt_write_total Total receipt write outcomes after policy or guard evaluation.
# TYPE chio_receipt_write_total counter
chio_receipt_write_total{outcome="allow"} 128
chio_receipt_write_total{outcome="deny"} 9
chio_receipt_write_total{outcome="pending_approval"} 3
chio_receipt_write_total{outcome="error"} 0
# TYPE chio_receipt_writer_healthy gauge
chio_receipt_writer_healthy 0
# TYPE chio_receipt_writer_liveness gauge
chio_receipt_writer_liveness{state="wedged"} 1

The sample values above are illustrative; the shape, the ordering, and the two gauge lines are exactly what the renderer emits. chio_receipt_writer_healthy is ReceiptWriterLiveness::healthy() rendered as 0 or 1, and that predicate is matches!(self, Self::Healthy | Self::Unknown). Read the two lines together: state="unknown" also reports healthy 1, so a host that has not wired an async writer and passes Unknown looks healthy on the gauge alone. The five states are healthy, saturated, wedged, dead, and unknown.

The shipped Prometheus rules consume this series in exactly one way. deploy/prometheus/chio-recording-rules.yml defines chio:receipt_write_error_ratio_5m, _1h, and _6h as the rate of outcome="error" over the clamped rate of the whole family, and chio-alert-rules.yml pages on a two-window burn (ChioReceiptWriteErrorBudgetBurn1h) and on absent_over_time(chio_receipt_write_total[10m]) (ChioReceiptWriteMetricsMissing), both at severity: p1. A conformance test reads those rule files as text and asserts the numerator: it fails the build if pending_approval appears anywhere in them, and if the family is counted as outcome!="success". Denials and pending approvals are normal traffic and must never enter an error budget.

Nothing in the tree serves this exposition over HTTP

render_mcp_edge_metrics_prometheus, render_a2a_edge_metrics_prometheus, and render_acp_edge_metrics_prometheus each return a String. Their only callers in the repository are the crates’ own tests and chio-conformance. No route anywhere under crates/ serves that body, and the string chio_receipt_write_total appears in no HTTP handler. The host embedding an edge is responsible for exposing it. Until it does, ChioReceiptWriteMetricsMissing is the alert that will fire. Node Observability covers the metric families a node does export on its own.

Choosing what a node terminates

There is no runtime switch that turns an edge on. Which protocols a node terminates is decided by which binary is running and, for an embedded host, by which edge type it constructs. The two shipped MCP listeners are chio mcp serve and chio mcp serve-http. An A2A or ACP edge is constructed in process: ChioA2aEdge::new(A2aEdgeConfig, Vec<ToolManifest>) or ChioAcpEdge::new(AcpEdgeConfig, Vec<ToolManifest>), with the host owning the socket or the pipe.

Construction validates before it publishes. Every manifest passes chio_manifest::validate_manifest first, and any failure rejects the whole edge. The A2A config is validated separately: agent name, version, endpoint URL, and protocol binding must each be non-empty and free of leading or trailing whitespace, and a violation fails construction rather than being trimmed. The ACP config is two fields, require_permission (default true) and default_category (default AcpCategory::Tool), and a published capability’s requires_permission is config.require_permission || tool.has_side_effects.

The edges: block in chio.yaml configures nothing

ChioConfig parses an edges array of EdgeConfig entries, each with an id, a protocol string, and an expose_from adapter id. validate_edges checks two things and only two: that ids are unique and non-empty, and that expose_from names a declared adapter. The protocol value is never checked against any known protocol, and no crate outside chio-config reads ChioConfig::edges at all. Adding an entry here starts no listener and binds no port. Treat it as a declaration the loader accepts, not as a runtime knob. The rest of the file is covered in chio.yaml Configuration.
chio.yamlyaml
adapters:
  - id: "petstore"
    protocol: "openapi"
    upstream: "http://localhost:8000"
    spec: "./petstore.yaml"

edges:
  - id: "mcp-bridge"
    protocol: "mcp"
    expose_from: "petstore"

The feature flags are the other half of the decision, and both edges ship with an empty default feature set. compatibility-surface compiles the passthrough into a non-test build; otel on chio-a2a-edge and chio-mcp-edge enables the GenAI span helpers; fuzz on chio-acp-edge and chio-mcp-edge exposes a libFuzzer entry point over the decode-then-dispatch pipeline and is enabled only by the standalone fuzz workspace. Auditing what a node terminates therefore means auditing the enabled feature set as well as the command line.


Guarantees and limits

StatusClaimEvidence
ShippedThree edges project the same kernel decision into MCP, A2A, and ACP envelopes, and each records one chio_receipt_write_total outcome per terminal response.record_receipt_write_verdict in each crate’s metrics.rs, all three delegating to chio_edge_metrics::ReceiptWriteCounters
ShippedA denied route is receipt-bearing. Route planning that returns Deny produces a kernel-signed response with full route and trace evidence, not an error return.The RouteSelectionDecision::Deny branch of CrossProtocolOrchestrator::execute, calling sign_planned_deny_response
ShippedA capability that crosses a protocol hop is narrowed to the concrete target server and tool, and the orchestrator refuses the call if the result is not a subset of the parent scope.attenuate_scope_for_tool; the BridgeError::InvalidAttenuation guard in orchestrator.rs
Proved by testCounters are isolated per edge. An ACP or A2A dispatch routed to an MCP target advances the source edge’s allow counter by exactly one and leaves the MCP edge’s unchanged.acp_edge_emits_chio_receipt_write_total and a2a_edge_emits_chio_receipt_write_total in crates/tooling/chio-conformance/tests/metrics_registry_consumed.rs
Proved by testpending_approval is a distinct normal-flow outcome and is excluded from the error-budget numerator, in the code and in the shipped rule files.receipt_write_recording_rules_only_count_infrastructure_errors, which reads deploy/prometheus/chio-recording-rules.yml as text
Proved by testAn ACP permission preview mirrors invoke-time DPoP policy without consuming the DPoP nonce the invoke will spend. Separately, a kernel configured for strict execution nonces makes a retry require a stable request id.permission_preview_accepts_valid_dpop_without_consuming_invocation_nonce in chio-acp-edge/src/tests/all.rs; strict_nonce_retries_require_and_accept_stable_request_ids in chio-acp-edge/src/tests/nonce_preflight.rs
LimitThe compatibility passthrough writes no receipt and no counter in any bucket. It is absent from the receipt-write series rather than represented in it.invoke_passthrough and handle_send_message_passthrough call no record_receipt_write* helper; passthrough_metadata hard-codes receiptId: null
LimitThe MCP edge applies no publication gate. x-chio-publish=false is silently ineffective there, and a duplicate tool name across manifests fails construction instead of being qualified or withheld.build_exposed_tool_bindings in chio-mcp-edge/src/runtime/discovery.rs; no BridgeFidelity or semantic_hints_for_tool import in the crate
LimitOnly three of the six DiscoveryProtocol families can be a bridge target. http, a2a, and acp have no registered executor on either bridged edge, so a tool naming one is withheld from A2A and ACP discovery. The MCP edge never reads the extension and lists the tool anyway.authoritative_target_registry in both bridge.rs files registers only McpTargetExecutor and OpenAiTargetExecutor
LimitDeferred lifecycles defer, they do not stream. A2A message/stream and ACP tool/stream store the request without calling the kernel; the terminal payload is collated and served on the follow-up call.complete_task in chio-a2a-edge/src/edge.rs; the stream_delivery fields on both authoritative lifecycle records
LimitDeferred task tables are per-process, in-memory, and lost on restart. Both are bounded at 1024 non-terminal tasks with a 5 minute TTL, and terminal tasks are exempt from the cap.MAX_DEFERRED_A2A_TASKS / DEFERRED_A2A_TASK_TTL_MILLIS and the ACP pair; ensure_deferred_task_capacity counts only non-terminal tasks
Not wiredNo route in the repository serves the edge Prometheus exposition. The renderers return a String that only tests and conformance consume; a host must publish it itself.Repository-wide search for chio_receipt_write_total outside the edge crates, the conformance test, and the two rule files
Not wiredThe edges block in chio.yaml starts nothing. Its protocol field is never validated against a known protocol, and no crate outside chio-config reads the parsed value.validate_edges in crates/platform/chio-config/src/validation.rs; no reader of ChioConfig::edges elsewhere
UnsupportedA CLI listener for A2A or ACP. Both crates build response values and hold no transport; the in-repo consumers are the conformance crate, the fuzz workspace, and the two worked examples, which loop over stdin.Workspace manifests declaring chio-a2a-edge and chio-acp-edge; serve_stdio in examples/hello-a2a/src/lib.rs
UnsupportedReaching the passthrough by accident. It compiles only under cfg(test) or an explicitly enabled compatibility-surface, and even then requires calling compatibility() to obtain the wrapper.default = [] in both crates’ [features]; ChioA2aEdgeCompatibility and ChioAcpEdgeCompatibility

Next Steps

  • Remote MCP Edge · the one edge that ships as a hosted listener, from bind through bearer mode to per-session kernel
  • Node Overview · the process these edges live in, and what it owes a cluster
  • Bridges · the normative per-protocol fidelity and receipt-continuity contract this page implements
  • Node Observability · the metric families a node exports without help, next to the one an edge hands you as a string
  • Route Plans · the signed record that pins a delegated dispatch to one bridge and one protocol target
Listeners & Edges · Chio Docs