Chio/Docs

OpenAPI Integration

Chio ingests OpenAPI specifications and enforces capability-based access control over HTTP APIs. This page covers the OpenAPI to manifest pipeline, the x-chio-* extension vocabulary, the default deny-by-method policy, and the chio api protect reverse proxy contract.

Source

This page normatively reflects spec/OPENAPI-INTEGRATION.md in the chio repository. Status: Normative. Version 1.0.

RFC 2119

The keywords MUST, SHOULD, and MAY are normative.

Pipeline Overview

The chio-openapi crate parses OpenAPI specifications and produces Chio ToolDefinition values conforming to chio.manifest.v1. Each HTTP operation (method + path pair) becomes one tool definition. The output drives both the OpenAPI-to-MCP bridge and the chio api protect reverse proxy.

  1. Parse the OpenAPI document (JSON or YAML).
  2. Extract operations from each path entry.
  3. Resolve $ref pointers and merge parameters.
  4. Read x-chio-* extensions for per-route policy hints.
  5. Generate ToolDefinition values and assign default policies.

Supported OpenAPI Versions

The parser MUST accept specifications declaring version 3.0.x or 3.1.x in the top-level openapi field. The value MUST begin with 3..

The parser MUST reject specifications with any other version prefix with an UnsupportedVersion error. OpenAPI 2.0 (Swagger) is not supported.

Supported Formats

The parser MUST accept both JSON and YAML input. Format detection is automatic: if the input (after leading whitespace) begins with {, the parser treats it as JSON. Otherwise, it treats the input as YAML. No explicit format flag is required from the caller.

Required Top-Level Fields

FieldError if absent
openapiMissingField("openapi")
infoMissingField("info")
pathsMissingField("paths")

When info.title is absent, the parser MUST default to "Untitled API". When info.version is absent, the parser MUST default to "0.0.0".


Operation to Tool Mapping

Route Extraction

For each entry in paths, the parser MUST extract operations for the following HTTP methods, in this order: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. Unrecognized method keys MUST be ignored.

Parameter Extraction

Each parameter MUST have a name and in field. The in field MUST be one of path, query, header, or cookie. Unknown values default to query.

Path parameters are always required, regardless of the required field value. For other locations, if required is absent, the parser defaults to false. Header and cookie parameters are parsed but MUST NOT appear in the generated ToolDefinition input schema. Only path and query parameters are promoted to tool input properties.

Path-level parameters and operation-level parameters MUST be merged before tool generation. If an operation-level parameter has the same name and location as a path-level parameter, the operation level wins.

Request Body Schema

When a requestBody is present, the parser MUST look for a content["application/json"].schema entry first. If absent, the parser MUST fall back to the first available content type. If the schema is a $ref, the parser resolves it before returning.

Response Schema Selection

The output schema for a ToolDefinition is derived from response schemas. The parser MUST prefer the 200 response, then 201, then any 2xx response that includes a schema. If no successful response includes a schema, the output schema is None.

$ref Resolution

The parser MUST resolve JSON Reference pointers that begin with #/. This covers #/components/schemas, #/components/parameters, and other #/components/ namespaces. The parser MUST reject:

  • External references (URIs not beginning with #/) with an UnresolvedRef error.
  • Internal references that point to nonexistent paths within the document with an UnresolvedRef error.

ToolDefinition Derivation

ToolDefinition fieldSource
nameoperationId if present; otherwise "{METHOD} {path}"
descriptionsummary if present, else description, else "{METHOD} {path}"
input_schemaJSON Schema object built from path + query parameters as properties, plus body property from request body schema
output_schemaSelected 2xx response schema
annotations.read_onlytrue if the operation has no side effects
annotations.destructivetrue if method is DELETE
annotations.idempotenttrue if method is GET, PUT, or DELETE
annotations.requires_approvalValue of x-chio-approval-required, defaulting to false
pricingNone (reserved for future use)

The input_schema MUST be a JSON Schema object with type: "object". Path and query parameters become top-level properties. If a parameter has no explicit schema, the generator defaults to { "type": "string" }. Required parameters appear in the required array. If a request body schema is present, it appears as a required property named "body".

Generator Configuration

OptionTypeDefaultEffect
server_idstring"openapi-server"Identifier for the generated manifest body
include_output_schemasbooleantrueWhether to derive output schemas from response definitions
respect_publish_flagbooleantrueWhether to honor x-chio-publish: false

x-chio-* Extension Vocabulary

Chio defines five vendor extension fields for OpenAPI operation objects. They provide per-route policy hints that the manifest generator and the chio api protect proxy consume. All x-chio-* fields are optional; absent values use the defaults below.

ExtensionScopeTypeDefaultMeaning
x-chio-sensitivityoperationenum stringinternalOne of public, internal, sensitive, restricted. Metadata classification consumed by the guard pipeline for logging level and audit granularity. Does not change policy directly.
x-chio-side-effectsoperationbooleanmethod-drivenOverrides the method-based side-effect default. true forces deny-by-default; false forces session-scoped allow.
x-chio-approval-requiredoperationbooleanfalseWhen true, forces deny-by-default regardless of method or x-chio-side-effects. Sets annotations.requires_approval.
x-chio-budget-limitoperationu64nonePer-invocation cost cap in minor currency units. Consumed by the budget guard.
x-chio-publishoperationbooleantrueControls whether the operation appears in the generated manifest. Useful for internal or health-check endpoints.

Sensitivity Levels

LevelMeaning
publicPublicly available data, no special handling
internalInternal data, logged but not restricted beyond defaults
sensitiveSensitive data, may trigger additional approval in the guard pipeline
restrictedHighly restricted data, always flagged for elevated scrutiny

If the value does not match one of the four allowed strings, the parser MUST ignore it (treat as absent).

Approval Precedence

Approval wins

x-chio-approval-required: true MUST take precedence over all other policy inputs. Even if x-chio-side-effects is false and the method is GET, the operation is deny-by-default when approval is required.

Extension Precedence Summary

Methodx-chio-side-effectsx-chio-approval-requiredResulting policy
GETabsentabsentSessionAllow
GETabsenttrueDenyByDefault
GETtrueabsentDenyByDefault
GETfalsetrueDenyByDefault
POSTabsentabsentDenyByDefault
POSTfalseabsentSessionAllow
POSTfalsetrueDenyByDefault
POSTabsenttrueDenyByDefault

Schema Preservation

OpenAPI schemas project as JSON Schema in MCP tools. The generator preserves the original schema shape for parameters and request bodies, with three normalizations:

  • All parameters are merged into a single { type: "object" } input schema.
  • Path and query parameters become top-level properties; required ones land in the required array.
  • Request body content (preferring application/json) appears under a top-level body property.

Internal $ref pointers are resolved before the schema is embedded in the tool definition; consumers see fully expanded schemas.

Example: Petstore

For the Petstore spec the pipeline produces four ToolDefinition values. Resulting policies:

OperationMethodPolicyReason
listPetsGETSessionAllowSafe method, no overrides
createPetPOSTDenyByDefaultSide-effect method
showPetByIdGETSessionAllowSafe method, no overrides
deletePetDELETEDenyByDefaultSide-effect method + x-chio-approval-required: true

Default Deny-by-Method Policy

Method Classification

CategoryMethodsDefault policy
Safe (read-only)GET, HEAD, OPTIONSSessionAllow
Side-effect (mutating)POST, PUT, PATCH, DELETEDenyByDefault

SessionAllow

Operations classified as SessionAllow are permitted by default within an active session. No capability token is required. The proxy MUST still generate a signed HttpReceipt for every SessionAllow request (audit receipt).

DenyByDefault

Operations classified as DenyByDefault require the caller to present a valid capability token. Without a token, the proxy MUST return a structured 403 response.

The caller presents a capability token via the X-Chio-Capability HTTP header or the chio_capability query parameter. When a valid token is present, the request proceeds to the upstream.

Extension Overrides

  1. If x-chio-approval-required is true, the result is always DenyByDefault. This check takes highest precedence.
  2. If x-chio-side-effects is explicitly set, it overrides the method default: true forces DenyByDefault, false forces SessionAllow.
  3. If neither extension is set, the method classification applies.

Unmatched Routes

If a request path does not match any route in the loaded OpenAPI spec, the proxy MUST fall back to method-based default policy. Safe methods receive SessionAllow; side-effect methods receive DenyByDefault.


Auth Pass-Through

The proxy extracts caller identity from request headers using the following precedence; only SHA-256 hashes are retained, never the raw credential.

PriorityHeaderIdentity format
1Authorization: Bearer <token>bearer:<truncated-sha256>
2X-Api-Key / X-API-Key / x-api-keyapikey:<truncated-sha256>
3(none)anonymous

When forwarding allowed requests, the proxy passes through Content-Type, Accept, and User-Agent headers and the request body verbatim. The maximum permitted body size is 10 MiB; the proxy MUST reject larger requests.


Error Mapping

Structured 403 Response

When a request is denied (DenyByDefault policy without a valid capability token), the proxy MUST return an HTTP 403 response with a JSON body conforming to this schema:

json
{
  "error": "chio_access_denied",
  "message": "<human-readable denial reason>",
  "receipt_id": "<receipt ID for the denial>",
  "suggestion": "provide a valid capability token in the X-Chio-Capability header or chio_capability query parameter"
}
FieldTypeDescription
errorstringAlways "chio_access_denied"
messagestringReason for denial from the guard evaluation
receipt_idstringID of the signed receipt that records this denial
suggestionstringActionable guidance for the caller

The Content-Type on the 403 response MUST be application/json.

Upstream Failure

If the upstream returns an error or the connection fails, the proxy MUST return HTTP 502 (Bad Gateway). The receipt for that request records the original allow verdict; the upstream failure does not change the access-control decision.

Error Catalog

chio-openapi errors:

ErrorCondition
InvalidJsonInput detected as JSON but failed to parse
InvalidYamlInput detected as YAML but failed to parse
MissingFieldA required top-level field (openapi, info, or paths) is absent
UnsupportedVersionThe openapi version does not begin with 3.
UnresolvedRefA $ref pointer could not be resolved (external URI or nonexistent internal path)

chio-api-protect errors:

ErrorCondition
SpecLoadThe OpenAPI spec file cannot be read or auto-discovery failed
SpecParseThe loaded spec failed OpenAPI parsing (wraps chio-openapi errors)
ConfigConfiguration error (e.g., cannot bind listen address)
ReceiptSignReceipt signing or content hashing failed
IoIO error during server operation
HttpClientHTTP client error during upstream communication

chio api protect Contract

chio api protect is a zero-code reverse proxy that interposes Chio capability-based access control between callers and an existing HTTP API. It requires no code changes to the upstream.

Command Interface

bash
chio api protect --upstream <URL> [--spec <path>] [--listen <addr>]
FlagRequiredDefaultDescription
--upstreamYes--Base URL of the upstream API to proxy to
--specNoAuto-discoverPath to a local OpenAPI spec file (JSON or YAML)
--listenNo127.0.0.1:9090Address and port for the proxy to bind

Spec Auto-Discovery

When --spec is not provided, the proxy MUST attempt to auto-discover the OpenAPI specification from the upstream server. The proxy probes the following well-known paths in order:

PriorityPath
1/openapi.json
2/openapi.yaml
3/swagger.json
4/api-docs

For each path, the proxy issues an HTTP GET request to {upstream}{path}. The first response that returns a successful HTTP status (2xx) with a non-empty body is used as the spec. If none of the probes succeed, the proxy MUST fail with a SpecLoad error directing the operator to use --spec.

Receipt Generation

The proxy MUST generate a signed HttpReceipt for every request, regardless of whether the request is allowed or denied.

Receipt fieldSource
idUnique receipt ID (UUIDv7)
request_idUnique request ID (UUIDv7)
route_patternMatched OpenAPI path pattern (e.g., /pets/{petId})
methodHTTP method
caller_identity_hashSHA-256 hash of the caller identity
verdictAllow or Deny with reason and guard name
evidenceGuard evaluation evidence chain
response_statusChio evaluation-time HTTP status (403 for denied; typically 200 for allowed sidecar/proxy evaluations before the upstream response exists)
timestampUnix timestamp of the request
content_hashHash of the request content
policy_hashSHA-256 hash of the loaded OpenAPI spec
kernel_keyPublic key of the proxy's ephemeral keypair

The receipt MUST be signed using the proxy's ephemeral keypair, generated at startup. The signature MUST be verifiable using the kernel_key field embedded in the receipt.

Receipt Header

For proxied responses (allowed requests forwarded upstream), the proxy MUST include an X-Chio-Receipt-Id header in the response. For 403 responses, the receipt ID appears in the JSON body as receipt_id and the header is not required.

Startup Behavior

  1. Load the OpenAPI spec (from --spec or via auto-discovery).
  2. Parse the spec using chio-openapi.
  3. Build a route table mapping (method, path pattern) to policy decisions.
  4. Generate an ephemeral signing keypair.
  5. Compute the SHA-256 hash of the spec content for use as policy_hash in receipts.
  6. Bind to the --listen address and begin accepting requests.

The proxy logs the number of routes loaded and the upstream URL at startup.


Implementation References

ComponentCrateEntry point
OpenAPI parserchio-openapicrates/chio-openapi/src/parser.rs
Manifest generatorchio-openapicrates/chio-openapi/src/generator.rs
Extension vocabularychio-openapicrates/chio-openapi/src/extensions.rs
Default policychio-openapicrates/chio-openapi/src/policy.rs
Reverse proxychio-api-protectcrates/chio-api-protect/src/proxy.rs
Request evaluatorchio-api-protectcrates/chio-api-protect/src/evaluator.rs
Spec discoverychio-api-protectcrates/chio-api-protect/src/spec_discovery.rs
Convenience functionchio-openapichio_openapi::tools_from_spec()