Chio/Docs
LOGIN · JOIN

Bindings API

Look up the canonical JSON, hashing, signing, and verification helpers the Chio SDKs share, with the C ABI and its ownership rules.

Reference chio@5f63e6e

On this page

The bindings contract fixes how a Chio SDK canonicalizes, hashes, signs, and verifies protocol objects, so a receipt checked in Python returns the same verification result as the Rust reference.

chio-binding-helpers defines the contract in Rust. The TypeScript, Python, and Go SDKs mirror its API shape, input and output rules, and error codes; another language links the C ABI or implements the contract against the shared conformance vectors. Transport, sessions, and authentication sit above this layer, in the language SDKs.

Synopsis

The crate root re-exports one module per concern. These snake_case names are the reference names; a binding renames them to its own convention. The smallest useful call verifies one stored receipt against the kernel key you pinned.

src/main.rs · in mainrust
use chio_binding_helpers::verify_receipt_json_with_trusted_signer_hex;

// receipt_json: one stored receipt; kernel_key_hex: the kernel key you pinned.
let verification =
    verify_receipt_json_with_trusted_signer_hex(&receipt_json, &[kernel_key_hex])?;

assert!(verification.signature_valid);
assert!(verification.signer_trusted);
assert!(verification.ok);
ModuleFunctionsTypes
canonical
canonicalize_json_str
capability
parse_capability_json, capability_body_canonical_json, verify_capability, verify_capability_json
CapabilityTimeStatus, CapabilityVerification
error
Error, ErrorCode, Result
hashing
sha256_hex_bytes, sha256_hex_utf8
manifest
parse_signed_manifest_json, signed_manifest_body_canonical_json, verify_signed_manifest, verify_signed_manifest_json
ManifestVerification
receipt
parse_receipt_json, receipt_body_canonical_json, verify_receipt, verify_receipt_json, verify_receipt_with_trusted_signers, verify_receipt_with_trusted_signer_hex, verify_receipt_json_with_trusted_signer_hex
ReceiptDecisionKind, ReceiptVerification
signing
is_valid_public_key_hex, is_valid_signature_hex, public_key_hex_matches, sign_utf8_message_ed25519, verify_utf8_message_ed25519, sign_json_str_ed25519, verify_json_str_signature_ed25519
CanonicalJsonSignature, Utf8MessageSignature

Scope

The contract covers canonical JSON, hashing, signing, and verification of protocol objects. Transport, authentication, session state, and orchestration live in the language SDKs.

What the contract owns

  • Canonical JSON helpers (RFC 8785).
  • SHA-256 hashing helpers for both bytes and UTF-8 strings.
  • Ed25519 signing and verification helpers.
  • Receipt parsing and verification helpers.
  • Capability parsing and verification helpers.
  • Signed manifest parsing and verification helpers.
  • Delegation-chain shape validity, returned as a field of capability verification and walked through chio-core.
  • Stable, bindings-oriented error codes.

Runtime and integration responsibilities

The language SDK or the embedding host owns these:

  • Session state machines.
  • Remote HTTP or stream transports.
  • Auth discovery, OAuth, token exchange, and token providers.
  • Task orchestration and nested callback routers.
  • Trust-control service clients.
  • Kernel execution, which belongs to the selected runtime or embedding host.

Public functions

Each module's functions, with the rule each one applies.

Canonical JSON

  • canonicalize_json_str(input: &str) -> Result<String>

RFC 8785 JSON Canonicalization Scheme. The helper sorts object keys lexicographically by UTF-16 code unit, emits numbers in ECMAScript Number.prototype.toString form, and writes no insignificant whitespace. Two inputs that parse to the same JSON value produce the same output.

Hashing

  • sha256_hex_bytes(input: &[u8]) -> String
  • sha256_hex_utf8(input: &str) -> String

SHA-256 over the input, hex-encoded lowercase. sha256_hex_utf8 calls sha256_hex_bytes on the UTF-8 encoding of the string, with no byte-order mark.

Ed25519 signing

  • is_valid_public_key_hex(value): parses as a Chio public key. Bare hex is read as Ed25519 (64 lowercase hex characters); a p256: or p384: prefix selects an ECDSA key, and a hybrid: prefix selects a hybrid classical and post-quantum key.
  • is_valid_signature_hex(value): parses as a Chio signature. Bare hex is read as Ed25519 (128 lowercase hex characters); the same p256:, p384:, and hybrid: prefixes select the matching algorithm.
  • public_key_hex_matches(left, right): case-insensitive equality after normalizing the hex. The comparison is an ordinary string comparison and runs in variable time.
  • sign_utf8_message_ed25519(input, seed_hex) -> Utf8MessageSignature
  • verify_utf8_message_ed25519(input, public_key_hex, signature_hex) -> bool
  • sign_json_str_ed25519(input, seed_hex) -> CanonicalJsonSignature
  • verify_json_str_signature_ed25519(input, public_key_hex, signature_hex) -> bool

The JSON helpers canonicalize the input before they sign or verify it, so an already-canonical JSON string and its non-canonical equivalent produce the same signature. CanonicalJsonSignature returns that canonical form alongside public_key_hex and signature_hex.

Receipts

  • parse_receipt_json(input) -> ChioReceipt
  • receipt_body_canonical_json(receipt) -> String
  • verify_receipt(receipt) -> ReceiptVerification
  • verify_receipt_json(input) -> ReceiptVerification
  • verify_receipt_with_trusted_signers(receipt, trusted_signers) -> ReceiptVerification
  • verify_receipt_with_trusted_signer_hex(receipt, trusted_signers) -> ReceiptVerification
  • verify_receipt_json_with_trusted_signer_hex(input, trusted_signers) -> ReceiptVerification

The helper checks the canonical-body signature against the embedded kernel_key, recomputes the receipt ID from the body, and compares the parameter hash with SHA-256 of the canonical action arguments. Signature verification dispatches on the key and signature prefix, accepting Ed25519 (bare hex), p256:, p384:, and hybrid: material.

verify_receipt calls verify_receipt_with_trusted_signers(receipt, &[]). The trusted-signer variants also require the receipt's kernel_key to appear in the supplied signer set. With an empty signer set the helper still reports each check, and signer_trusted, ok, and authorized are false.

ReceiptVerification carries signature_valid, parameter_hash_valid, receipt_id_valid, decision (a ReceiptDecisionKind of Allow, Deny, Cancelled, Incomplete, or None), receipt_kind, boundary_class, trust_level, result, authorized, signer_key_hex, signer_trusted, and ok. Read the fields this way:

  • signature_valid, parameter_hash_valid, receipt_id_valid: the three integrity checks. Read them individually when diagnosing a failure.
  • signer_trusted: the embedded signer matches a key in the supplied, nonempty trust set.
  • ok: integrity and signer trust passed. A valid denial can set it to true.
  • authorized: integrity and signer trust passed for a recorded mediated decision at the prevent boundary with an Allow decision.
  • result: a label derived from receipt semantics and decision, separate from the aggregate verification result.

A valid trace or advisory receipt is evidence and leaves authorized false. authorized describes the recorded decision; authorizing a new request and checking its freshness happen at the kernel.

Capabilities

  • parse_capability_json(input) -> CapabilityToken
  • capability_body_canonical_json(capability) -> String
  • verify_capability(capability, now_secs: u64, max_delegation_depth: Option<u32>) -> CapabilityVerification
  • verify_capability_json(input, now_secs: u64, max_delegation_depth: Option<u32>) -> CapabilityVerification

verify_capability takes the parsed token, the current Unix timestamp in seconds, and an optional maximum delegation depth. The returned CapabilityVerification carries signature_valid, delegation_chain_shape_valid, time_valid, and a time_status of valid, not_yet_valid, or expired.

src/main.rs · in mainrust
use std::time::{SystemTime, UNIX_EPOCH};

use chio_binding_helpers::{parse_capability_json, verify_capability};

let cap = parse_capability_json(&json)?;
let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
let max_delegation_depth = Some(8_u32);

let verification = verify_capability(&cap, now, max_delegation_depth)?;
assert!(verification.signature_valid);
assert!(verification.delegation_chain_shape_valid);
assert!(verification.time_valid);

The delegation-chain walk validates chain shape: each per-link signature, delegator-to-delegatee continuity, timestamp ordering, and chain length against max_delegation_depth. The walk leaves parent and child scope comparison, scope attenuation, and chain binding to the stricter chio-core path, which this call never invokes. That is why the field is named delegation_chain_shape_valid.

The crate exposes no standalone verify_delegation_chain or check_attenuation. A binding calls the verification helper and reads the field; direct chain and attenuation inspection lives in chio-core.

Signed manifests

  • parse_signed_manifest_json(input) -> SignedManifest
  • signed_manifest_body_canonical_json(signed_manifest) -> String
  • verify_signed_manifest(signed_manifest) -> ManifestVerification
  • verify_signed_manifest_json(input) -> ManifestVerification

ManifestVerification carries structure_valid, signature_valid, embedded_public_key_valid, and embedded_public_key_matches_signer.

C ABI

chio-bindings-ffi wraps the same helpers behind a C calling convention. Its Cargo.toml declares crate-type = ["staticlib", "cdylib", "rlib"], so one build produces a static archive, a shared library, and a Rust library. A language without a Rust toolchain links the shared library and calls the symbols below.

Two #[repr(C)] structs carry every result. ChioFfiBuffer is a ptr and a len. ChioFfiResult is a status, an error_code, and a data buffer. Status is CHIO_FFI_STATUS_OK, CHIO_FFI_STATUS_ERROR, CHIO_FFI_STATUS_PANIC, or CHIO_FFI_STATUS_NULL_ARGUMENT, and error_code is the numeric form of the error codes below. The caller frees every returned buffer with chio_buffer_free.

SymbolParametersReturnsBehavior
chio_buffer_free
ChioFfiBuffer
void
Releases a buffer the library allocated. The caller owns every buffer it receives.
chio_ffi_abi_version
()
u32
Returns CHIO_FFI_ABI_VERSION.
chio_ffi_build_info
()
ChioFfiResult
JSON with crate_name, crate_version, abi_version, target, and features.
chio_canonicalize_json
input_json
ChioFfiResult
canonicalize_json_str.
chio_sha256_hex_utf8
input_utf8
ChioFfiResult
sha256_hex_utf8.
chio_sha256_hex_bytes
input, input_len
ChioFfiResult
sha256_hex_bytes over a pointer and length.
chio_sign_utf8_message_ed25519
input_utf8, seed_hex
ChioFfiResult
sign_utf8_message_ed25519, serialized as JSON.
chio_verify_utf8_message_ed25519
input_utf8, public_key_hex, signature_hex
ChioFfiResult
verify_utf8_message_ed25519, serialized as the string true or false.
chio_sign_json_ed25519
input_json, seed_hex
ChioFfiResult
sign_json_str_ed25519, serialized as JSON.
chio_verify_json_signature_ed25519
input_json, public_key_hex, signature_hex
ChioFfiResult
verify_json_str_signature_ed25519, serialized as the string true or false.
chio_verify_capability_json
input_json, now_secs, max_delegation_depth
ChioFfiResult
verify_capability_json. Pass CHIO_FFI_NO_MAX_DELEGATION_DEPTH for no limit.
chio_verify_receipt_json
input_json
ChioFfiResult
verify_receipt_json, serialized as JSON.
chio_verify_receipt_json_with_trusted_signers
input_json, trusted_signers_json
ChioFfiResult
verify_receipt_json_with_trusted_signer_hex. The signer set arrives as a JSON array of strings.
chio_verify_manifest_json
input_json
ChioFfiResult
verify_signed_manifest_json, serialized as JSON.

The table holds the 14 exported symbols, every #[no_mangle] pub extern "C" item in the crate. Pointer arguments are C strings unless a length parameter follows. String entrypoints reject a null argument with CHIO_FFI_STATUS_NULL_ARGUMENT. For byte input, a null pointer is accepted only when the length is zero. A non-null pointer must reference readable input for the entire call; null checks cannot establish that a foreign pointer is valid.

Input and output rules

Prefer these shapes at the binding boundary:

  • JSON-string input for structured payloads.
  • UTF-8 string input for signed text helpers.
  • Byte-slice input when byte identity matters.
  • Verification result structs, so a caller reads which check failed.

Keep these out of it:

  • Deep internal Rust types.
  • Opaque handles, unless they wrap reusable compiled objects.
  • Async APIs in the invariants layer.
  • Ownership-sensitive runtime state.

Error codes

ErrorCode enumerates the codes, and Error::code maps every error the crate raises onto one of them. The enum serializes in snake_case, so the serialized value is the code below. An SDK exposes it through a typed error class. Depend on the code, never on the Rust enum spelling or message text; removing or renaming a code breaks that dependency.

  • invalid_public_key: Public key hex is malformed or the wrong length.
  • invalid_hex: Hex decoding failed.
  • invalid_signature: Signature hex is malformed or the wrong length.
  • json: JSON parsing failed.
  • canonical_json: RFC 8785 canonicalization refused the input.
  • capability_expired: Capability is past expires_at.
  • capability_not_yet_valid: Capability is before issued_at.
  • capability_revoked: Capability appears in a revocation list.
  • delegation_chain_broken: A link in the delegation chain fails its signature check.
  • attenuation_violation: Child scope exceeds parent scope.
  • scope_mismatch: The requested action is not within the capability scope.
  • signature_verification_failed: Signature verification returned false.
  • delegation_depth_exceeded: Chain depth exceeds the supplied limit.
  • invalid_hash_length: A hash field is not 32 bytes.
  • merkle_proof_failed: A Merkle inclusion proof did not reproduce the root.
  • empty_tree: A Merkle operation was requested on an empty tree.
  • invalid_proof_index: A Merkle proof index is out of range.
  • empty_manifest: A signed manifest has no tools.
  • duplicate_tool_name: A signed manifest contains two tools with the same name.
  • invalid_tool_name: A manifest tool name is empty or otherwise invalid.
  • invalid_input_schema: A tool input schema is not a JSON object.
  • invalid_output_schema: A tool output schema is not a JSON object.
  • duplicate_server_tool: A server-tool allowlist entry appears more than once.
  • invalid_manifest_field: A required manifest field is missing or malformed.
  • invalid_required_permission: A declared required permission is empty or malformed.
  • duplicate_required_permission: A required permission is declared more than once.
  • unsupported_schema: The schema identifier is unknown to this binding.
  • manifest_verification_failed: The manifest body signature failed to verify.

Cross-language conformance

The corpus lives at tests/bindings/vectors/, one directory per family, each with a v1.json of inputs and expected outputs. MANIFEST.sha256 pins the bytes of every vector file, so a binding compares against the same corpus the Rust reference does.

FamilyCasesCovers
canonical/v1.json
Key ordering by UTF-16 code unit, escaping, i64 and u64 boundaries, subnormals, negative zero, and supplementary-plane keys.
capability/v1.json
20
Signature, time-bound, and delegation-chain cases, including the depth limit and the inclusive issued_at and expires_at boundaries.
eval/v1.json
The golden chio.eval-report.bundle.v1 bundle, which every binding must reproduce byte for byte.
hashing/v1.json
SHA-256 over UTF-8 inputs: empty, block-aligned FIPS lengths, an embedded null, combining marks, and a supplementary-plane emoji.
manifest/v1.json
Signed manifests: valid, tampered signature, mismatched embedded key, duplicate tool name, unsupported schema, and empty tools.
receipt/v1.json
14
Allow and deny receipts, a wrong parameter hash, a tampered signature, and the trace and advisory receipt kinds.
signing/v1.json
Ed25519 over UTF-8 messages and over canonical JSON, across several seeds and at 64-byte and 128-byte block boundaries.

Cross-language verification requires RFC 8785 canonical JSON. Every binding implements key sorting, whitespace removal, ECMAScript number formatting, and the specified handling of -0, infinities, and lone surrogates. chio conformance run runs the corpus against a peer adapter and compares every result with the Rust reference. Fetch the published peer, or pass your own binding with --peer-binary.

Terminalbash
chio conformance fetch-peers --language python
chio conformance run --peer python
Output
peer        python  ./.chio-peers/python/chio-conformance-peer
canonical   pass
capability  pass
eval        pass
hashing     pass
manifest    pass
receipt     pass
signing     pass
conformant  python matches the Rust reference on every vector

Every Chio release also runs each SDK's own suite against the corpus before it is tagged; the source tests list them.

Change rules

The crate doc comment fixes the boundary: session runtime, transport, auth, and callback orchestration stay in the language-native SDKs. Three rules follow from it.

  • A new public entrypoint carries an owning use case, a unit test, or both.
  • A change that widens scope into transport or runtime behavior is rejected by default.
  • An SDK consumes helpers through this facade. chio-core and chio-manifest are declared in Cargo.toml so the facade can reach them; a binding reads the facade.

Versioning

Both crates take the workspace version, 0.1.0, and set publish = false, so an SDK reaches them through a path dependency inside the workspace. The C ABI carries a second number: CHIO_FFI_ABI_VERSION, returned by chio_ffi_abi_version and repeated in the chio_ffi_build_info JSON, which a caller reads to confirm the struct layout it compiled against.

Implementation sources

The Rust helper source defines the contract in src/canonical.rs, src/hashing.rs, src/signing.rs, src/receipt.rs, src/capability.rs, src/manifest.rs, and src/error.rs, where ErrorCode carries serde(rename_all = "snake_case").

The C ABI implementation is crates/sdk/chio-bindings-ffi/src/lib.rs and its Cargo.toml. The conformance corpus is tests/bindings/vectors/.

Next: read the receipt fields these helpers verify