Chio/Docs
LOGIN · JOIN

ReferenceSpec

Bindings API

The chio-binding-helpers contract at workspace version 0.1.0: canonical JSON, hashing, signing, verification, the C ABI, and the cross-language vectors.

Source

The crate is the contract. This page reflects crates/sdk/chio-binding-helpers in the chio repository: src/canonical.rs, src/hashing.rs, src/signing.rs, src/receipt.rs, src/capability.rs, src/manifest.rs, and src/error.rs. The C ABI comes from crates/sdk/chio-bindings-ffi/src/lib.rs and its Cargo.toml. The conformance corpus is tests/bindings/vectors/.

Status: shipped. Both crates carry the workspace version, 0.1.0, and set publish = false, so an SDK reaches them through a path dependency inside the workspace rather than from a registry.


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.

ModuleFunctionsTypes
canonicalcanonicalize_json_str
capabilityparse_capability_json, capability_body_canonical_json, verify_capability, verify_capability_jsonCapabilityTimeStatus, CapabilityVerification
errorError, ErrorCode, Result
hashingsha256_hex_bytes, sha256_hex_utf8
manifestparse_signed_manifest_json, signed_manifest_body_canonical_json, verify_signed_manifest, verify_signed_manifest_jsonManifestVerification
receiptparse_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_hexReceiptDecisionKind, ReceiptVerification
signingis_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_ed25519CanonicalJsonSignature, Utf8MessageSignature

Scope

chio-binding-helpers fixes the contract in Rust. The TypeScript, Python, and Go SDKs mirror the same API shape, the same input and output rules, and the same error codes. 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.

What the language SDKs own

  • 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.
  • The kernel execution runtime.

Public functions

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. It is an ordinary string comparison, not a constant-time one.
  • 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

A receipt verifies when its canonical-body signature checks against the embedded kernel_key and the parameter hash in the receipt equals 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 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, and it does not authorize a call.

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. 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 stricter path in chio-core that this call does not invoke, which is why the field is named delegation_chain_shape_valid.

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(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.

Where chain and attenuation checks live

chio-binding-helpers exposes no standalone verify_delegation_chain or check_attenuation. Delegation-chain validity comes back as a field of CapabilityVerification, and direct chain and attenuation inspection lives in chio-core. A binding calls the verification helper and reads the field; it does not re-walk the chain.

C ABI

crates/sdk/chio-bindings-ffi wraps the same helpers behind a C calling convention. Its Cargo.toml declares crate-type = ["staticlib", "cdylib", "rlib"], so the workspace build produces a static archive, a shared library, and a Rust library from one crate. 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 code table below. The caller frees every returned buffer with chio_buffer_free.

SymbolParametersReturnsBehavior
chio_buffer_freeChioFfiBuffervoidReleases a buffer the library allocated. The caller owns every buffer it receives.
chio_ffi_abi_version()u32Returns CHIO_FFI_ABI_VERSION.
chio_ffi_build_info()ChioFfiResultJSON with crate_name, crate_version, abi_version, target, and features.
chio_canonicalize_jsoninput_jsonChioFfiResultcanonicalize_json_str.
chio_sha256_hex_utf8input_utf8ChioFfiResultsha256_hex_utf8.
chio_sha256_hex_bytesinput, input_lenChioFfiResultsha256_hex_bytes over a pointer and length.
chio_sign_utf8_message_ed25519input_utf8, seed_hexChioFfiResultsign_utf8_message_ed25519, serialized as JSON.
chio_verify_utf8_message_ed25519input_utf8, public_key_hex, signature_hexChioFfiResultverify_utf8_message_ed25519, serialized as the string true or false.
chio_sign_json_ed25519input_json, seed_hexChioFfiResultsign_json_str_ed25519, serialized as JSON.
chio_verify_json_signature_ed25519input_json, public_key_hex, signature_hexChioFfiResultverify_json_str_signature_ed25519, serialized as the string true or false.
chio_verify_capability_jsoninput_json, now_secs, max_delegation_depthChioFfiResultverify_capability_json. Pass CHIO_FFI_NO_MAX_DELEGATION_DEPTH for no limit.
chio_verify_receipt_jsoninput_jsonChioFfiResultverify_receipt_json, serialized as JSON.
chio_verify_receipt_json_with_trusted_signersinput_json, trusted_signers_jsonChioFfiResultverify_receipt_json_with_trusted_signer_hex. The signer set arrives as a JSON array of strings.
chio_verify_manifest_jsoninput_jsonChioFfiResultverify_signed_manifest_json, serialized as JSON.

The table holds 14 exported symbols, every #[no_mangle] pub extern "C" item in crates/sdk/chio-bindings-ffi/src/lib.rs. Pointer arguments are C strings unless a length parameter follows, and a null pointer returns CHIO_FFI_STATUS_NULL_ARGUMENT rather than dereferencing it.

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.
  • Verification result structs, so a caller reads which check failed.

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.

Error codes

ErrorCode in src/error.rs enumerates the codes, and Error::code maps every error the crate raises onto one of them. The enum carries serde(rename_all = "snake_case"), so the serialized value is the code below. An SDK exposes it through a typed error class. Depend on the code, not on the Rust enum spelling or any message text. Removing or renaming a code breaks that dependency.

CodeMeaning
invalid_public_keyPublic key hex is malformed or the wrong length.
invalid_hexHex decoding failed.
invalid_signatureSignature hex is malformed or the wrong length.
jsonJSON parsing failed.
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 its signature check.
attenuation_violationChild scope exceeds parent scope.
scope_mismatchThe requested action is not within the capability scope.
signature_verification_failedSignature verification returned false.
delegation_depth_exceededChain depth exceeds the supplied limit.
invalid_hash_lengthA hash field is not 32 bytes.
merkle_proof_failedA Merkle inclusion proof did not reproduce the root.
empty_treeA Merkle operation was requested on an empty tree.
invalid_proof_indexA Merkle proof index is out of range.
empty_manifestA signed manifest has no tools.
duplicate_tool_nameA signed 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_schemaThe schema identifier is unknown to this binding.
manifest_verification_failedThe 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.jsonKey ordering by UTF-16 code unit, escaping, i64 and u64 boundaries, subnormals, negative zero, and supplementary-plane keys.
capability/v1.json20Signature, time-bound, and delegation-chain cases, including the depth limit and the inclusive issued_at and expires_at boundaries.
eval/v1.jsonThe golden chio.eval-report.bundle.v1 bundle, regenerated by cargo run -p xtask -- eval-receipt-regen.
hashing/v1.jsonSHA-256 over UTF-8 inputs: empty, block-aligned FIPS lengths, an embedded null, combining marks, and a supplementary-plane emoji.
manifest/v1.jsonSigned manifests: valid, tampered signature, mismatched embedded key, duplicate tool name, unsupported schema, and empty tools.
receipt/v1.json14Allow and deny receipts, a wrong parameter hash, a tampered signature, and the trace and advisory receipt kinds.
signing/v1.jsonEd25519 over UTF-8 messages and over canonical JSON, across several seeds and at 64-byte and 128-byte block boundaries.

Each language runs the corpus through its own test command.

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

Canonical JSON requirement

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.

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 rather than reaching into chio-core or chio-manifest directly. The two dependencies are declared in Cargo.toml so the facade can reach them; a binding reads the facade.

Versioning

Both crates take their version from the workspace manifest, currently 0.1.0, and both set publish = false. 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.

  • TypeScript SDK (@chio-protocol/sdk): the transport, session, and auth layers over this contract.
  • Python SDK (chio-sdk, imported as chio)
  • Go SDK (github.com/backbay-labs/chio/sdks/go/chio-go)
  • Rust SDK: the same crate as a dependency, with the guard SDK beside it.
  • Receipt Format: the field semantics the receipt helpers verify.