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.
| Module | Functions | Types |
|---|---|---|
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
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]) -> Stringsha256_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); ap256:orp384:prefix selects an ECDSA key, and ahybrid: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 samep256:,p384:, andhybrid: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) -> Utf8MessageSignatureverify_utf8_message_ed25519(input, public_key_hex, signature_hex) -> boolsign_json_str_ed25519(input, seed_hex) -> CanonicalJsonSignatureverify_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) -> ChioReceiptreceipt_body_canonical_json(receipt) -> Stringverify_receipt(receipt) -> ReceiptVerificationverify_receipt_json(input) -> ReceiptVerificationverify_receipt_with_trusted_signers(receipt, trusted_signers) -> ReceiptVerificationverify_receipt_with_trusted_signer_hex(receipt, trusted_signers) -> ReceiptVerificationverify_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) -> CapabilityTokencapability_body_canonical_json(capability) -> Stringverify_capability(capability, now_secs: u64, max_delegation_depth: Option<u32>) -> CapabilityVerificationverify_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.
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) -> SignedManifestsigned_manifest_body_canonical_json(signed_manifest) -> Stringverify_signed_manifest(signed_manifest) -> ManifestVerificationverify_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.
| Symbol | Parameters | Returns | Behavior |
|---|---|---|---|
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 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.
| Code | Meaning |
|---|---|
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.
| Family | Cases | Covers |
|---|---|---|
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, regenerated by cargo run -p xtask -- eval-receipt-regen. | |
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. |
Each language runs the corpus through its own test command.
$ 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 ./... # GoCanonical JSON requirement
-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-coreorchio-manifestdirectly. The two dependencies are declared inCargo.tomlso 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.
Related
- TypeScript SDK (
@chio-protocol/sdk): the transport, session, and auth layers over this contract. - Python SDK (
chio-sdk, imported aschio) - 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.