Chio/Docs

ReferenceSpec

HTTP Substrate

A local Chio sidecar evaluates HTTP requests against policy, signs a receipt, and returns a structured verdict.

When to use this page

Use this page when: you are building or auditing an HTTP-layer SDK, framework middleware, or sidecar deployment and need the typed ChioHttpRequest, HttpReceipt, verdict enum, default policy, and the deterministic HttpReceipt ChioReceipt mapping. Don't use this if: you want the agent-protocol wire format (see Wire Protocol) or the shared capability and receipt contract (see Protocol Reference).

Source

This page normatively reflects spec/HTTP-SUBSTRATE.md in the chio repository. Status: Normative. Version 1.0. The keywords MUST, SHOULD, and MAY are normative per RFC 2119.


Overview

The HTTP sidecar protocol uses a local evaluation model: a Chio kernel runs as a local process and exposes an HTTP API on localhost. Language-specific middleware (Express, Actix, Axum, etc.) intercepts incoming HTTP requests, constructs a ChioHttpRequest, sends it to the sidecar, and enforces the returned verdict.

The sidecar protocol defines:

  • A sidecar evaluation protocol (three HTTP endpoints)
  • A typed request model (ChioHttpRequest) for policy evaluation
  • A typed receipt model (HttpReceipt) for signed proof of evaluation
  • Supporting types for caller identity, authentication, sessions, and verdicts
  • A deterministic mapping from HttpReceipt to the core ChioReceipt type

Sidecar Evaluation Protocol

Transport

The sidecar MUST listen on 127.0.0.1:9090 by default. Implementations MAY override the sidecar URL via:

  • An explicit configuration value (sidecarUrl in SDK config, or equivalent)
  • The CHIO_SIDECAR_URL environment variable

If both are set, the explicit configuration value takes precedence. If neither is set, the default http://127.0.0.1:9090 MUST be used. All request and response bodies MUST use Content-Type: application/json.

Endpoints

EndpointMethodPurpose
/chio/evaluatePOSTEvaluate an HTTP request against policy
/chio/verifyPOSTVerify a receipt signature
/chio/healthGETSidecar health check

POST /chio/evaluate

Evaluates an HTTP request against the loaded policy and returns a signed receipt.

Request body: ChioHttpRequest.

Response body: EvaluateResponse.

FieldTypeRequiredDescription
verdictVerdictMUSTThe evaluation verdict
receiptHttpReceiptMUSTSigned receipt that records the evaluation
evidenceGuardEvidence[]MUSTGuard evidence collected during evaluation
execution_nonceSignedExecutionNonce or nullMAYPresent only when the kernel is configured for strict/opt-in nonce mode and issues one (allow verdict). The client MUST re-present it as ToolCallRequest.execution_nonce before executing the tool call. Absent on deny/cancel and on deployments without a nonce config.

Status codes:

  • 200 OK. Evaluation completed (regardless of verdict). The verdict field indicates whether the request was allowed or denied.
  • 400 Bad Request. The request body is malformed or missing required fields.
  • 500 Internal Server Error. The sidecar encountered an internal error during evaluation.

200 means evaluated, not allowed

Implementations MUST return 200 for both allow and deny verdicts. The HTTP status code reflects the health of the evaluation pipeline, not the policy outcome. The policy outcome is encoded in the verdict field.

POST /chio/verify

Verifies the signature, content-addressed id, and authority semantics of a previously issued HttpReceipt.

Request body: HttpReceipt.

Response body: VerifyReceiptResponse. There is no single valid field; the response decomposes verification into these fields:

FieldTypeDescription
signature_validbooleanWhether the receipt signature verifies against kernel_key
signer_trustedbooleanWhether the signing key is in the verifier's trusted set
receipt_id_validbooleanWhether id matches the recomputed content-addressed id
parameter_hash_validbooleanWhether content_hash is a well-formed 64-char lowercase hex digest
receipt_kindstringThe receipt's semantic class
boundary_classstringThe receipt's runtime boundary class
trust_levelstringThe receipt's trust level
resultstringVerdict result: allow, deny, cancelled, or incomplete
authorizedbooleanok and the verdict is allow. This is the field to gate execution on.
signer_key_hexstringHex encoding of the signing key
okbooleanRoll-up: signature valid, signer trusted, id valid, parameter hash valid, and authority semantics well-formed

A receipt with a valid signature but an expired timestamp still reports signature_valid: true. Temporal validity is the caller's responsibility.

GET /chio/health

FieldTypeRequiredDescription
statusstringMUSTOne of "healthy", "degraded", or "unhealthy"
versionstringMUSTSidecar version string
receipt_backendstringMUSTBackend of the embedded kernel's receipt log: durable or ephemeral
revocation_backendstringMUSTBackend of the embedded kernel's revocation state: durable, remote, or ephemeral

Status codes: 200 OK if healthy or degraded; 503 Service Unavailable if unhealthy.

Timeout and Failure Behavior

SDKs MUST implement a configurable timeout for sidecar calls. The default timeout SHOULD be 5000 milliseconds.

Fail-closed by default

When the sidecar is unreachable or times out, SDKs MUST default to fail-closed behavior (deny the request). SDKs MAY expose a configuration option to override this to fail-open, but fail-closed MUST be the default.

ChioHttpRequest

The protocol-agnostic HTTP request that Chio evaluates. This is the shared input type for all HTTP sidecar adapters (reverse proxy, framework middleware, and sidecar alike).

FieldTypeRequiredDefaultDescription
request_idstringMUSTUnique request identifier. UUIDv7 recommended.
methodHttpMethodMUSTHTTP method of the request
route_patternstringMUSTMatched route pattern (e.g., "/pets/{petId}")
pathstringMUSTActual request path (e.g., "/pets/42")
querymap<string, string>MAY{}Query parameters
headersmap<string, string>MAY{}Selected headers. Adapters MUST NOT include raw auth credential headers.
callerCallerIdentityMUSTExtracted caller identity
body_hashstring or nullMAYnullSHA-256 hex hash of the request body. null for bodyless requests (GET, HEAD, OPTIONS).
body_lengthintegerMAY0Content-Length of the request body in bytes
session_idstring or nullMAYnullSession ID this request belongs to
capability_idstring or nullMAYnullCapability token ID presented with this request
timestampintegerMUSTUnix timestamp (seconds) when the request was received

Content Hash Computation

The content hash that binds a request to its receipt is computed as the SHA-256 hex digest of the canonical JSON (RFC 8785) of the following binding object:

json
{
  "body_hash": "<body_hash or null>",
  "method": "<HTTP method>",
  "path": "<actual path>",
  "query": { "<sorted query params>" },
  "route_pattern": "<route pattern>"
}

CallerIdentity

The identity of the caller as extracted from the HTTP request.

FieldTypeRequiredDefaultDescription
subjectstringMUSTStable identifier for the caller
auth_methodAuthMethodMUSTHow the caller authenticated
verifiedbooleanMUSTfalseWhether this identity has been cryptographically verified
tenantstring or nullMAYnullTenant or organization the caller belongs to
agent_idstring or nullMAYnullAgent identifier when the caller is an AI agent

Identity hash: the caller_identity_hash in receipts is the SHA-256 hex digest of the canonical JSON representation of the full CallerIdentity object. The same identity MUST always produce the same hash.

AuthMethod

A tagged union representing how the caller authenticated. The discriminator field is method. Variant names use snake_case.

VariantTag valueFieldsDescription
Bearer"bearer"token_hash: stringBearer token (JWT or opaque). Implementations MUST NOT store or transmit raw tokens.
ApiKey"api_key"key_name, key_hashAPI key. SHA-256 hex hash of the key value.
Cookie"cookie"cookie_name, cookie_hashSession cookie. SHA-256 hex hash of the cookie value.
MtlsCertificate"mtls_certificate"subject_dn, fingerprintmTLS client certificate. SHA-256 fingerprint.
Anonymous"anonymous"(none)No authentication was presented.
json
{ "method": "bearer", "token_hash": "a1b2c3d4e5f6..." }

SessionContext

Per-session context carried through the Chio HTTP pipeline. A session groups related requests from the same caller over a bounded time window.

FieldTypeRequiredDefaultDescription
session_idstringMUSTUnique session identifier
callerCallerIdentityMUSTAuthenticated caller for this session
created_atintegerMUSTUnix timestamp (seconds) when the session was created
expires_atinteger or nullMAYnullUnix timestamp (seconds) when the session expires
request_countintegerMAY0Number of requests evaluated in this session
bytes_readintegerMAY0Cumulative bytes read by this session
bytes_writtenintegerMAY0Cumulative bytes written by this session
delegation_depthintegerMAY0Current delegation depth. 0 means direct caller.
metadataobject or nullMAYnullExtensibility metadata

Verdict

Internally tagged with "verdict" as the tag key and snake_case variant names.

VariantTag valueFieldsDescription
Allow"allow"(none)Request is allowed. Proceed to upstream.
Deny"deny"reason, guard, http_status, detailsRequest is denied. Default http_status is 403. details is a boxed DenyDetails block, omitted from the wire when empty.
Cancel"cancel"reasonEvaluation was cancelled (e.g., timeout, circuit breaker).
Incomplete"incomplete"reasonEvaluation did not reach a terminal state.

Fail-closed semantics

When the verdict is cancel or incomplete, middleware MUST treat the request as denied. Only an explicit allow verdict permits forwarding to the upstream API.

When deserializing a deny verdict without an explicit http_status field, implementations MUST default to 403.

DenyDetails

A deny verdict carries an optional boxed DenyDetails block so a sidecar can tell an SDK exactly what scope was required versus granted. Every field is optional; the whole details key is omitted from the wire when all fields are empty.

FieldTypeDescription
tool_namestring or nullTool that was denied
tool_serverstring or nullTool server that hosts the denied tool
requested_actionstring or nullShort summary of the attempted action
required_scopestring or nullScope the kernel says is required, as a canonical ToolGrant(...) string
granted_scopestring or nullScope the presented capability actually had. Null when none was presented.
reason_codestring or nullStable machine-readable code, e.g. scope.missing
receipt_idstring or nullReceipt id capturing this denial, for audit correlation
hintstring or nullNext-steps sentence for the developer
docs_urlstring or nullLink to the docs page explaining this deny code

HttpMethod

Serialized as uppercase strings.

ValueSafeRequires Capability
"GET"YesNo
"HEAD"YesNo
"OPTIONS"YesNo
"POST"NoYes
"PUT"NoYes
"PATCH"NoYes
"DELETE"NoYes

Safe methods (GET, HEAD, OPTIONS) are considered side-effect-free by default and receive session-scoped allow verdicts. Unsafe methods (POST, PUT, PATCH, DELETE) require an explicit capability grant.

GuardEvidence

FieldTypeRequiredDescription
guard_namestringMUSTName of the guard
verdictbooleanMUSTWhether the guard passed (true) or denied (false)
detailsstring or nullMAYHuman-readable details about the guard's decision

HttpReceipt

A signed receipt proving that an HTTP request was evaluated by the Chio kernel. The receipt binds the request identity, route, method, verdict, semantic classification, and guard evidence under a signature from the kernel (Ed25519 by default; P-256 and P-384 also supported).

FieldTypeRequiredDefaultSignedDescription
idstringMUSTYesContent-addressed receipt id: SHA-256 hex of the canonical JSON of the receipt body with id removed (compute_http_receipt_id), computed and assigned inside HttpReceipt::sign. Not a UUIDv7 and not caller-assignable.
request_idstringMUSTYesUnique request ID this receipt covers
route_patternstringMUSTYesMatched route pattern
methodHttpMethodMUSTYesHTTP method of the evaluated request
caller_identity_hashstringMUSTYesSHA-256 hex hash of the caller identity
session_idstring or nullMAYnullYesSession ID the request belonged to
verdictVerdictMUSTYesThe kernel's verdict
receipt_kindReceiptKindMUSTYesSigned semantic class. Always mediated_decision for HTTP receipts.
boundary_classBoundaryClassMUSTYesSigned runtime boundary. Always prevent for HTTP receipts.
observation_outcomeObservationOutcome or nullMUST NOTnullYesRequired absent on mediated HTTP receipts; signing rejects a present value.
tool_originToolOriginMUSTYesWhere the protected effect executes relative to Chio (caller_executed, host_executed_provider_reported, host_executed_unmediated)
redaction_modeRedactionModeMUSTYesRedaction applied to signed details (none, summary, redacted)
actor_chainActorRef[]MAY[]YesSigned actor attribution for the HTTP decision. Omitted from the wire when empty.
evidenceGuardEvidence[]MAY[]YesPer-guard evidence collected during evaluation
response_statusintegerMUSTYesHTTP status Chio associated with the evaluation outcome at receipt-signing time.
timestampintegerMUSTYesUnix timestamp (seconds) when the receipt was created
content_hashstringMUSTYesSHA-256 hex hash binding the request content to this receipt
policy_hashstringMUSTYesSHA-256 hex hash of the policy that was applied
trust_levelTrustLevelMUSTYesStrength of kernel mediation. Always mediated for HTTP receipts.
capability_idstring or nullMAYnullYesCapability ID that was exercised, if any
metadataobject or nullMAYnullYesExtensibility metadata
kernel_keyPublicKey (hex)MUSTYesKernel's algorithm-aware public key (the same chio_core_types::crypto::PublicKey as the core receipt): bare 64-hex Ed25519, p256:<130-hex>, or p384:<194-hex>
signatureSignature (hex)MUSTNoAlgorithm-aware signature over canonical JSON of the body fields: bare 128-hex Ed25519, or a p256:/p384:-prefixed form

Signing

To produce a signed receipt:

  • Construct an HttpReceiptBody containing all fields from the table above except signature.
  • Compute the content-addressed id (compute_http_receipt_id) and validate the authority semantics (mediated_decision + prevent + mediated, no observation_outcome).
  • Serialize the body to canonical JSON (RFC 8785).
  • Sign the canonical bytes with the kernel's private key (Ed25519 by default; P-256 or P-384 when configured).
  • Attach the resulting signature to the receipt.

Verification

  • Extract the body (all fields except signature).
  • Serialize the body to canonical JSON (RFC 8785).
  • Verify the signature against the canonical bytes using the kernel_key embedded in the receipt.

Implementations SHOULD verify the receipt signature before treating receipt content as authoritative.

Cryptographic Representations

kernel_key and signature reuse the same algorithm-aware chio_core_types::crypto types as the core ChioReceipt (imported directly), so they are not restricted to bare Ed25519 hex:

  • kernel_key: bare 64-character lowercase hex for Ed25519, or a p256:<130-hex> / p384:<194-hex> prefixed form.
  • signature: bare 128-character lowercase hex for Ed25519, or a p256: / p384: prefixed DER-hex form. Verification dispatches off the self-describing encoding.

HttpReceipt to ChioReceipt Mapping

An HttpReceipt maps into the core ChioReceipt type for unified storage and querying. There are two methods, and the no-argument one fails closed by design:

  • to_chio_receipt() (no argument) always returns an error ("cannot convert HttpReceipt into signed ChioReceipt without the kernel keypair"), because a copied HTTP signature would not verify over the ChioReceiptBody.
  • to_chio_receipt_with_keypair(keypair) builds the ChioReceiptBody, recomputes content_hash over that canonical body, and re-signs via ChioReceipt::sign(...) with the supplied kernel keypair. The old HTTP signature is never copied.

Field Mapping

HttpReceipt fieldChioReceipt fieldTransformation
ididCopied directly
timestamptimestampCopied directly
capability_idcapability_idUnwrapped; defaults to empty string if null
(constant)tool_serverSet to "http"
method + route_patterntool_nameFormatted as "{method} {route_pattern}"
method, route_pattern, request_idaction.parametersJSON object { method, route, request_id }
(derived)action.parameter_hashCanonical hash of action.parameters (via ToolCallAction::from_parameters), so a durable store accepts the converted receipt — not the HTTP content_hash
verdictdecisionConverted via Verdict.to_decision()
receipt_kind, boundary_class, observation_outcome, tool_origin, redaction_mode, actor_chain(same fields)Copied directly
content_hashcontent_hashRecomputed as SHA-256 of the canonical JSON of the ChioReceiptBody
policy_hashpolicy_hashCopied directly
evidenceevidenceCopied directly
metadatametadataCopied directly
trust_leveltrust_levelCopied directly
(constant)tenant_idSet to null
kernel_keykernel_keyCopied directly
(re-signed)signatureFreshly produced by ChioReceipt::sign(...) with the supplied keypair. The HTTP signature is never copied.

Verdict to Decision Mapping

Verdict variantDecision variant
allowallow
deny { reason, guard, http_status }deny { reason, guard } (http_status is dropped)
cancel { reason }cancelled { reason }
incomplete { reason }incomplete { reason }

Conversion Re-Signs, or Fails Closed

No-argument conversion fails closed

to_chio_receipt() takes no keypair and unconditionally returns an error. A ChioReceipt signature cannot be derived from an HttpReceipt alone, so the conversion refuses rather than emit a receipt whose signature would fail standard verification.

Use to_chio_receipt_with_keypair(keypair) for conversion. It recomputes content_hash over the canonical ChioReceiptBody and re-signs with the kernel keypair, producing a ChioReceipt whose signature and action.parameter_hash verify, so a durable receipt store accepts it.


Default Policy Semantics

The HTTP sidecar protocol defines default policy behavior based on HTTP method safety:

  • Safe methods (GET, HEAD, OPTIONS): Session-scoped allow.
  • Unsafe methods (POST, PUT, PATCH, DELETE): Deny by default. These methods require an explicit capability token presented in the X-Chio-Capability request header or via capability_id in the ChioHttpRequest.

When a route is not matched in the loaded policy, the evaluator MUST fall back to method-based default policy.


Error Handling

Sidecar Error Codes

SDKs MUST use the following error codes when communicating sidecar failures to callers:

CodeMeaning
chio_access_deniedThe request was denied by policy
chio_sidecar_unreachableThe sidecar process is not reachable
chio_evaluation_failedThe sidecar returned a non-200 status
chio_invalid_receiptReceipt verification failed
chio_timeoutThe sidecar did not respond within the timeout

Structured Error Response

When middleware denies a request, the response body SHOULD be a structured JSON object:

FieldTypeRequiredDescription
errorstringMUSTError code from the table above
messagestringMUSTHuman-readable error message
receipt_idstring or nullMAYReceipt ID for the denied evaluation
suggestionstring or nullMAYActionable suggestion for the caller

Schemas and Conformance

Versioned HTTP sidecar schemas live under spec/schemas/chio-http/v1/. Schema files in that directory are the machine-readable contract for the HTTP sidecar types. Implementations MUST serialize all HTTP sidecar types in a form accepted by those schemas. Schema validation SHOULD be exercised against live Rust serialization, not handwritten examples alone.

FileDescribed type
http-receipt.schema.jsonHttpReceipt
chio-http-request.schema.jsonChioHttpRequest
caller-identity.schema.jsonCallerIdentity with AuthMethod
verdict.schema.jsonVerdict
evaluate-request.schema.jsonPOST /chio/evaluate request body
evaluate-response.schema.jsonPOST /chio/evaluate response body