Chio/Docs

ReferenceSpec

Bindings API

The shared low-level contract for chio SDKs: canonical JSON, hashing, signing, verification, and cross-language conformance.

Not a transport layer

The bindings contract covers canonical JSON, hashing, signing, and verification of protocol objects. It does not cover transport, authentication, session state, orchestration, or any runtime concern. Those layers live in the per-language SDKs.

Scope

The upstream crate chio-binding-helpers fixes this contract in Rust. Every other SDK (TypeScript, Python, Go) mirrors the same API shape, the same input and output rules, and the same stable error codes.

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 validity as part of capability verification (walked via chio-core).
  • Stable, bindings-oriented error codes.

The Contract Does Not Own

  • Session state machines.
  • Remote HTTP or stream transports.
  • Auth discovery, OAuth, token exchange, token providers.
  • Task orchestration or nested callback routers.
  • Trust-control service clients.
  • Kernel execution runtime.

Public API

Each binding must expose the functions below under its language-native naming convention. The snake_case names used here are the canonical reference names from chio-binding-helpers.

Canonical JSON

  • canonicalize_json_str(json: string) -> string

RFC 8785 JSON Canonicalization Scheme. Object keys are sorted lexicographically by UTF-16 code unit. Numbers are emitted in ECMAScript Number.prototype.toStringform. No insignificant whitespace. Output is deterministic for any input that parses to the same JSON value.

Hashing

  • sha256_hex_bytes(bytes) -> string
  • sha256_hex_utf8(text: string) -> string

SHA-256 over the input, hex-encoded lowercase. The UTF-8 form hashes the bytes of the UTF-8 encoding of the string without a BOM.

Ed25519 Signing

  • is_valid_public_key_hex(hex): 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-plus-post-quantum key.
  • is_valid_signature_hex(hex): 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(a, b): case-insensitive equality after normalizing the hex. This is an ordinary string comparison, not constant-time.
  • sign_utf8_message_ed25519(message, seed_hex) -> Utf8MessageSignature
  • verify_utf8_message_ed25519(message, signature, public_key_hex) -> bool
  • sign_json_str_ed25519(json, seed_hex) -> CanonicalJsonSignature
  • verify_json_str_signature_ed25519(json, signature, public_key_hex) -> bool

JSON signing canonicalizes the input before signing. Signing an already-canonical JSON string and signing its non-canonical equivalent must produce the same signature.

Receipts

  • parse_receipt_json(json) -> ChioReceipt
  • receipt_body_canonical_json(receipt) -> string
  • verify_receipt(receipt) -> ReceiptVerification
  • verify_receipt_json(json) -> ReceiptVerification
  • verify_receipt_with_trusted_signers(receipt, trusted_signers) -> ReceiptVerification
  • verify_receipt_with_trusted_signer_hex(receipt, trusted_signer_hex) -> ReceiptVerification
  • verify_receipt_json_with_trusted_signer_hex(json, trusted_signer_hex) -> ReceiptVerification

A receipt verifies when its canonical-body signature checks against the embedded kernel_key and the parameter hash in the receipt matches SHA-256 of the canonical action arguments. Signature verification is algorithm-aware: it dispatches on the key and signature prefix, accepting Ed25519 (bare hex), p256:, p384:, and hybrid: material. verify_receipt is verify_receipt_with_trusted_signers(receipt, &[]); the trusted-signer variants additionally require the receipt's kernel_key to appear in the supplied signer set.

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. authorized is true only when the receipt is a mediated decision at the prevent boundary with an Allow decision and the signature, parameter-hash, receipt-id, and trusted-signer checks all pass. A valid trace or advisory receipt is evidence, never authorization.

Capabilities

  • parse_capability_json(json) -> CapabilityToken
  • capability_body_canonical_json(cap) -> string
  • verify_capability(cap, now: u64, max_delegation_depth: Option<u32>) -> CapabilityVerification
  • verify_capability_json(json, now: 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. The delegation-chain walk validates chain shape: each per-link signature, delegator-to-delegatee continuity, timestamp ordering, and chain length against max_delegation_depth. It does not compare parent and child scopes. Scope-attenuation and chain-binding enforcement is a separate, stricter path in chio-core that this binding call does not invoke, which is why the field is named delegation_chain_shape_valid rather than an attenuation-valid flag.

rust
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);

Signed Manifests

  • parse_signed_manifest_json(json) -> SignedManifest
  • signed_manifest_body_canonical_json(manifest) -> string
  • verify_signed_manifest(manifest) -> ManifestVerification
  • verify_signed_manifest_json(json) -> ManifestVerification

Delegation-chain and Merkle helpers are planned

Standalone exports such as verify_delegation_chain, check_attenuation, and merkle_proof_verify are not part of the current chio-binding-helpers API. Today, delegation-chain validity is returned as a field of CapabilityVerification; direct chain and attenuation inspection lives in chio-core. Merkle inclusion-proof verification for receipt stores is planned after the wire shape stabilizes.

Input and Output Rules

Prefer:

  • JSON-string input for structured payloads.
  • UTF-8 string input for signed text helpers.
  • Byte-slice input when byte identity matters.
  • Explicit verification result structs (not raw booleans) when policy or receipt detail matters.

Avoid:

  • Exposing deep internal Rust types across the binding boundary.
  • Opaque handles unless they wrap reusable compiled objects.
  • Async APIs in the invariants layer.
  • Ownership-sensitive runtime state.

Stable Error Taxonomy

SDKs expose errors through a typed class that carries one of the snake_case error codes below. Depend on the code value, not on the Rust enum spelling or any message text.

CodeMeaning
invalid_public_keyPublic key hex is malformed or wrong length.
invalid_hexGeneric hex-decode failure.
invalid_signatureSignature hex is malformed or wrong length.
jsonJSON parse failure.
canonical_jsonRFC 8785 canonicalization refused the input.
capability_expiredCapability is past expires_at.
capability_not_yet_validCapability is before issued_at.
capability_revokedCapability appears in a revocation list.
delegation_chain_brokenA link in the delegation chain fails signature check.
attenuation_violationChild scope exceeds parent scope.
scope_mismatchRequested action not within the capability scope.
signature_verification_failedEd25519 verification returned false.
delegation_depth_exceededChain depth exceeds the per-binding limit.
invalid_hash_lengthA hash field is not 32 bytes.
merkle_proof_failedMerkle inclusion proof did not reproduce the root.
empty_treeMerkle operation requested on an empty tree.
invalid_proof_indexMerkle proof index is out of range.
empty_manifestSigned manifest has no tools.
duplicate_tool_nameSigned manifest contains two tools with the same name.
invalid_tool_nameA manifest tool name is empty or otherwise invalid.
invalid_input_schemaA tool input schema is not a JSON object.
invalid_output_schemaA tool output schema is not a JSON object.
duplicate_server_toolA server-tool allowlist entry appears more than once.
invalid_manifest_fieldA required manifest field is missing or malformed.
invalid_required_permissionA declared required permission is empty or malformed.
duplicate_required_permissionA required permission is declared more than once.
unsupported_schemaSchema identifier is unknown to this binding.
manifest_verification_failedManifest body signature failed to verify.

Cross-Language Conformance

The conformance harness lives in the Chio monorepo. It ships a corpus of input vectors and expected outputs for every function above, plus a runner per language that compares the binding's output to the Rust reference byte for byte.

bash
$ cargo test -p chio-conformance              # Rust reference
$ cd sdks/typescript/chio-ts && npm test      # TypeScript
$ cd sdks/python/chio-py && pytest            # Python
$ cd sdks/go/chio-go && go test ./...         # Go

The suite covers:

  • Canonical JSON for Unicode edge cases, nested objects, floats, and integer boundaries.
  • SHA-256 vectors across byte and UTF-8 inputs including non-BMP characters.
  • Ed25519 sign and verify with seeds from the RFC 8032 test vectors plus protocol-specific cases.
  • Receipt verification against signatures produced by the Rust kernel.
  • Capability verification and delegation chains of depth 0, 1, 3, and the depth limit.
  • Signed manifest verification over the spec fixtures.

Canonical JSON requirement

Cross-language verification requires RFC 8785 canonical JSON. Every binding must implement key sorting, whitespace removal, ECMAScript number formatting, and the specified handling of -0, infinities, and lone surrogates.

Change Rules

The contract is frozen under the rules below. They keep the bindings contract separate from transport and runtime behavior.

  • New public entrypoints require an owning use case, a unit test, or both.
  • Changes that widen scope into transport or runtime behavior are rejected by default.
  • Error code removals or renames are breaking changes and require a major bump.
  • SDKs should consume helpers through this facade rather than reaching into chio-core or chio-manifest directly.

Versioning

The Bindings API follows semantic versioning. The current major version is 1. Patch releases add tests and fix bugs without changing the public API. Minor releases add new helpers or new error codes without removing or renaming existing ones. Major releases are rare and are coordinated with an upgrade guide that walks every SDK through the change.

Deferred Work

The following work is deferred pending package-backed remote-edge parity. It depends on the pure-TypeScript and pure-Python paths being available in production.

  • chio-bindings-ffi: C ABI bindings for embedding in non-Rust hosts.
  • chio-bindings-wasm: WASM module with JS glue.
  • chio-native: Native-library companion to the pure-language SDKs.
  • Go CGO bridge work.
  • Policy compilation helpers beyond plain verification.

Per-Language SDK References