Rust SDK
Run agents on a computer from Rust, then build on the crates beneath it: evidence verification, custom guards, and the decision core.
The chio crate carries the Computer API from the Quickstart. Beneath it sit focused crates for local evidence verification, guard extensions, host adapters, and the portable decision core. Choose the boundary your application needs.
| You need to | Crate or interface | Where it runs |
|---|---|---|
Run agents on a computer | Your application, talking to your kernel; grants, runs, receipts, and apply. | |
Buy checked work from a partner | Your application, talking to your owner work service; selection, agreement, submit, and the work view. | |
Verify receipts, capabilities, or manifests | Local pure functions; no kernel process or network connection. | |
Write a custom policy guard | Inside a WASM guard component loaded by the kernel. | |
Embed HTTP evaluation in a Rust service | Application process; host supplies identity, durable state, and execution integration. | |
Embed the portable decision core | Native, mobile, or WASM host with explicit adapters. | |
Cross a C ABI or verify evaluation bundles | Foreign- |
0 entries. Choose a section or search the full text.
Install
cargo add chioThe crate builds on Rust 1.94 or newer, edition 2021. Each crate below publishes to crates.io under its own name.
Computer
Computer is the API the Quickstart uses. A computer is a versioned environment where authority, resources, and work live. You open one, fork it, grant an agent part of the fork, run work, read the signed receipts, and apply the changes you keep. Your kernel enforces every grant and signs every decision.
Calls that reach the kernel are async and return chio::Result. Methods with options return builders that implement IntoFuture, so .await sends the call and chained methods set options first. grant and reissue are synchronous. Event streams implement futures::Stream; chio::stream::StreamExt brings next() into scope. #[chio::main] starts the runtime, so a binary needs no other dependency.
This program is Quickstart part 1 in one file. It runs inside the computer that chio new quickstart creates.
use chio::stream::StreamExt;
use chio::{Computer, Grant, USD};
#[chio::main]
async fn main() -> chio::Result<()> {
let project = Computer::open("quickstart").await?;
let fixer = project.agent("fixer").await?;
let fork = project.fork().await?;
let authority = fork.grant(
Grant::to(&fixer)
.resource("/workspace", ["read", "write"])
.resource("/models/default", ["invoke"])
.budget(USD(2))
.expires_in("20m"),
)?;
let mut run = fixer.run("fix issue #412").on(&fork).authority(authority).start().await?;
let mut events = run.events();
while let Some(event) = events.next().await {
println!("{}", event?.summary());
}
let refusal = project.history().receipts().outcome("deny").first().await?;
println!("{}", refusal.describe());
Ok(())
}00:00 admitted fixer runs "fix issue #412" on fork 51c07ae 00:03 read /workspace/src/pricing.py 00:05 read /workspace/tests/test_pricing.py 00:09 refused read /secrets/.env: outside the grant 00:12 invoke /models/default, USD 0.21 00:41 write /workspace/src/pricing.py 00:56 test pytest -k issue_412: passed spent USD 0.48 of 2.00 receipt rcpt-01a8…9c04 request fixer read /secrets/.env verdict deny: outside the grant grant cap-01a8…31f2 policy sha256 3c7b…8010 signed your kernel 4f1c…a2e9, signature verifies
The examples below continue from it: project is the quickstart computer, fixer is its agent, and fork and authority are the fork and grant above. The service examples use the pricing service from Quickstart part 2, and the partner examples use the partner card, treaty, and reviews mailbox from part 3.
Computer::open
async fn Computer::open(name: &str) -> chio::Result<Computer>
Opens a computer your operator identity owns and returns its handle. The handle reads the computer's current revision; nothing is copied.
use chio::Computer;
let project = Computer::open("quickstart").await?;
println!("{}", project.describe());computer quickstart
owner did:chio:z6Mkf3…Qp9 (you)
revision main @ 7c2e1d0
resources /workspace git, 14 files
/models/default model route, metered in USD
/secrets 3 filesComputer::connect
async fn Computer::connect(name: &str) -> chio::Result<Partner>
Resolves a paired partner by the name you gave its card, and authenticates its kernel before returning.
let partner = Computer::connect("partner").await?;
println!("{}", partner.describe());partner partner, did:chio:z6MkpA7…c2T operator Halden, halden.example kernel 9b07…d31c, pinned from its card transport iroh, authenticated at handshake
Computer::partners
async fn Computer::partners() -> chio::Result<Vec<Partner>>
Lists every partner you have paired with, each with its pinned kernel key and the treaty in force.
for partner in Computer::partners().await? {
println!("{}", partner.summary());
}partner did:chio:z6MkpA7…c2T Halden kernel 9b07…d31c treaty trt-01aa…4c19
Computer::agent
async fn agent(&self, name: &str) -> chio::Result<Agent>
Returns the agent with this name, issuing it an identity from your kernel the first time. An agent holds no authority until you grant it some.
let fixer = project.agent("fixer").await?;
println!("{}", fixer.describe());agent fixer, did:chio:z6MkrT2…8wd issued by your kernel 4f1c…a2e9 grants none
Computer::agents
fn agents(&self, role: &str) -> AgentsBuilder; .count(n).reads(&mailbox).start().await -> chio::Result<Vec<Agent>>
Starts several agents of one role, numbered from 1, optionally reading a shared mailbox.
let reviewers = project.agents("review").count(2).reads(&reviews).start().await?;
for reviewer in &reviewers {
println!("{}", reviewer.summary());
}reviewer-1 did:chio:z6MkvR1…3mc reads reviews reviewer-2 did:chio:z6MkvR2…9tf reads reviews
Computer::fork
fn fork(&self) -> ForkBuilder; .boot(program).await -> chio::Result<Fork>
Branches the computer's managed state at its current revision. Work on the fork stays there until you apply it. A fork does not reset authority, history, or spending.
let fork = project.fork().await?;
println!("{}", fork.describe());fork 51c07ae of quickstart, base main 7c2e1d0 writes stay in the fork until you apply them
Fork::grant
fn grant(&self, grant: Grant) -> chio::Result<Authority>
Issues a capability token over part of the fork: resources and their rights, a budget charged at your door, and an expiry. Grant::to takes an agent, a partner, or a DID; .delegation(...) lets the holder delegate narrower grants onward, and .under(&treaty) issues a grant within a treaty. Issuance completes durably before the call returns.
let authority = fork.grant(
Grant::to(&fixer)
.resource("/workspace", ["read", "write"])
.resource("/models/default", ["invoke"])
.budget(USD(2))
.expires_in("20m"),
)?;
println!("{}", authority.describe());grant cap-01a8…31f2, signed by your kernel
to fixer, did:chio:z6MkrT2…8wd
on quickstart, fork 51c07ae
allows /workspace read, write
/models/default invoke
budget USD 2.00, charged at your door
expires in 20 minutesAuthority::reissue
fn reissue(&self, on: &Fork) -> chio::Result<Authority>; fn reissue_with(&self, on: &Fork, edit: impl FnOnce(Grant) -> Grant) -> chio::Result<Authority>
Issues a grant on another fork with the same terms. reissue_with edits the terms first, to change the resources, budget, or lifetime. Your kernel signs the new grant; the original is unchanged.
let next = project.fork().await?;
let again = authority.reissue(&next)?;
println!("{}", again.describe());grant cap-01a8…5b3e, signed by your kernel
to fixer, did:chio:z6MkrT2…8wd
on quickstart, fork 7d2e914
allows /workspace read, write
/models/default invoke
budget USD 2.00, charged at your door
expires in 20 minutesAuthority::delegate
fn delegate(&self, grant: Grant) -> chio::Result<Authority>
Issues a child grant from this one to another agent. The child can drop resources, rights, money, and time, never add them, and its budget is held from the parent's. The parent must carry a delegation allowance. A child that asks for more raises chio::Error::Attenuation, and nothing is issued.
// authority was granted with .delegation(Delegation::depth(1).fanout(2))
let tester = project.agent("tester").await?;
let child = authority.delegate(
Grant::to(&tester)
.resource("/workspace", ["read"])
.budget(Money::cents(50))
.expires_in("10m"),
)?;
println!("{}", child.describe());grant cap-01d2…6c13, delegated by fixer from cap-01d2…2a90 to tester, did:chio:z6MkvH5…3qe allows /workspace read budget USD 0.50, held from the fixer's USD 2.00 expires in 10 minutes chain you, fixer, tester
Authority::revoke
async fn revoke(&self) -> chio::Result<()>
Revokes the grant and every grant delegated from it. The kernel checks each ancestor at admission, so a run under any of them is refused from its next call.
authority.revoke().await?;revoked cap-01d2…2a90, by you cascade cap-01d2…6c13 (tester), refused from its next call
Authority::token
fn token(&self) -> &CapabilityToken
The signed capability token behind the grant, in its wire form. The Computer and the Kernel reads one field by field.
println!("{}", serde_json::to_string_pretty(authority.token())?);{
"id": "cap-01a8…31f2",
"issuer": "4f1c…a2e9",
"subject": "b81e…40c7",
"scope": { "grants": [ 3 tool grants with path_prefix constraints ] },
"issued_at": 1791763200,
"expires_at": 1791764400,
"signature": "a3f9…0d52"
}Agent::run
fn run(&self, task: &str) -> RunBuilder; .on(&fork).authority(authority).start().await -> chio::Result<Run>
Sends a task to an agent under a grant. Every operation the agent attempts reaches your kernel before it reaches a resource.
let mut run = fixer.run("fix issue #412").on(&fork).authority(authority).start().await?;
println!("{}", run.describe());run run-01a8…e7c0 agent fixer, on fork 51c07ae authority cap-01a8…31f2
Run::events
fn events(&mut self) -> EventStream; .include(&agents); impl Stream<Item = chio::Result<RunEvent>>
Streams the run's events in order: admissions, reads, writes, model calls, tests, and refusals. A refusal is an event with a receipt, not an exception, and the run continues.
let mut events = run.events();
while let Some(event) = events.next().await {
println!("{}", event?.summary());
}00:00 admitted fixer runs "fix issue #412" on fork 51c07ae 00:03 read /workspace/src/pricing.py 00:05 read /workspace/tests/test_pricing.py 00:09 refused read /secrets/.env: outside the grant 00:12 invoke /models/default, USD 0.21 00:41 write /workspace/src/pricing.py 00:56 test pytest -k issue_412: passed spent USD 0.48 of 2.00
Partner::exec
fn exec(&self, task: &str) -> ExecBuilder; .on(&fork).authority(a).report_to(&mailbox).start().await -> chio::Result<Run>
Sends a task to a partner's agents under a grant issued within a treaty. The partner's kernel admits the work and runs it in its own process tree. run.join_tree() waits for every agent the partner delegated to.
let exec = partner.exec("fix issue #414").on(&fork).authority(authority);
let mut run = exec.report_to(&reviews).start().await?;
let mut events = run.events();
while let Some(event) = events.next().await {
println!("{}", event?.summary());
}
run.join_tree().await?;00:00 them admitted "fix issue #414" under trt-01aa…4c19 00:01 them delegate coordinator to fixer-1, fixer-2, tester, each narrower 00:31 them refused fixer-2 read /secrets/.env: outside the grant, at your door 01:47 them sealed fork c4e81b7 at 5b93d10 spent USD 3.12 of 5.00 across 4 of their agents
Fork::diff
async fn diff(&self) -> chio::Result<Diff>
Returns the changes the fork holds against its base, as a unified diff.
println!("{}", fork.diff().await?);src/pricing.py
@@ def discount(price, percent):
+ if not 0 <= percent <= 100:
+ raise ValueError("percent must be between 0 and 100")
return price * (1 - percent / 100)Computer::apply
fn apply(&self, changes: ChangeSet) -> ApplyBuilder; .treaty(&treaty).await -> chio::Result<Applied>
Publishes the fork's exact changes if main still matches the fork's base, and returns the signed merge. Under a treaty, both kernels co-sign it.
let merged = project.apply(fork.changes()).await?;
println!("{}", merged.describe());applied fork 51c07ae onto quickstart main revision main 8d4b2e7, base 7c2e1d0 receipt rcpt-01a8…d51e, signed by your kernel
History::receipts
fn receipts(&self) -> ReceiptQuery; .outcome(o).agent(name).rule(name).service(name).since(d); .first().await, .all().await, or .export(dir)
Reads signed receipts from the computer's history, filtered by outcome, agent, rule, service, or time. Every filter combines with the others.
let refusal = project.history().receipts().outcome("deny").first().await?;
println!("{}", refusal.describe());receipt rcpt-01a8…9c04 request fixer read /secrets/.env verdict deny: outside the grant grant cap-01a8…31f2 policy sha256 3c7b…8010 signed your kernel 4f1c…a2e9, signature verifies
Receipt
impl Receipt { fn id(&self) -> &str; fn request(&self) -> &str; fn summary(&self) -> String; fn raw(&self) -> &str; fn describe(&self) -> String }
One signed decision. summary is one line for listings, describe is the full card, and raw is the canonical JSON your kernel signed.
println!("{}", refusal.raw());{"action":{"parameter_hash":"5e0d…71c2","parameters":{"path":"/secrets/.env"}},"capability_id":"cap-01a8…31f2","decision":{"reason":"not in capability scope","verdict":"deny"},"evidence":[],"id":"rcpt-01a8…9c04","kernel_key":"4f1c…a2e9","policy_hash":"3c7b…8010","receipt_kind":"mediated_decision","signature":"9d2e…07b4","tool_name":"read","tool_server":"quickstart"}History::export
fn export(&self, dir: impl AsRef<Path>) -> chio::Result<Evidence>; ReceiptQuery::export(self, dir) -> chio::Result<Evidence>
Writes the computer's history, or one agent's part of it, to a directory as an evidence package. Verification checks every signature against pinned kernel keys, offline.
let evidence = project.history().export("audit/")?;
println!("{}", evidence.verify()?);receipts 9 signed decisions from your kernel refusals 1: fixer read /secrets/.env spend USD 0.48 of 2.00 verified offline, against your kernel key 4f1c…a2e9
Evidence::verify_ledger
fn verify(&self) -> chio::Result<Verification>; fn verify_ledger(&self) -> chio::Result<Verification>
Checks every signature in an exported evidence package against your pinned kernel key, offline. verify_ledger also rebuilds both money ledgers from the signed records: each budget hold must close as committed, released, or retained with nothing outstanding, and each escrowed deposit must satisfy paid + refunded + locked + free = deposited. A hold whose outcome is unknown stays retained until it is resolved. Money Conservation states both identities.
let evidence = project.history().export("audit/")?;
println!("{}", evidence.verify_ledger()?);receipts 9 signed decisions from your kernel holds 3 on cap-01a8…31f2, each with one terminal disposition or a retained outcome budget reserved 2.40 = committed 0.21 + released 1.39 + retained 0.80 + outstanding 0.00 retained rcpt-01a8…c311: outcome unknown, hold kept until resolved funding 1 deposit: paid 200 + refunded 60 + locked 0 + free 40 = deposited 300 verified offline, against your kernel key 4f1c…a2e9
Spend::stream
fn spend(&self) -> Spend; fn stream(&self) -> SpendStream; .capability(id).cursor(c) implements Stream<Item = chio::Result<SpendEvent>>
Streams spend events from your kernel's signed receipts, optionally for one capability lineage. Each event copies one receipt's financial metadata, bound to it by digest, and carries its sequence number. Pass an earlier cursor to resume: the cursor is opaque and binds your tenant, the filter, the last sequence scanned, and the store generation, and a stale cursor yields a typed gap rather than skipping ahead. Comptroller Console covers webhooks and burn rate.
let mut events = project.spend().stream().capability("cap-01d2…6c13");
while let Some(event) = events.next().await {
println!("{}", event?.summary());
}seq 4308 15:46 triage-418 models USD 0.02 remaining USD 10.04 of USD 20.00 seq 4309 15:46 triage-419 models USD 0.02 remaining USD 10.02 of USD 20.00 seq 4310 15:47 triage-417 models USD 0.02 remaining USD 10.00 of USD 20.00 cursor 9c1d…77ae, complete through seq 4310, checkpoint 43
Policy::add
async fn add(&self, rule: Rule) -> chio::Result<RuleRef>; Rule::new(name).deny_write(glob) or .egress_allow(hosts)
Adds a named rule to the computer's policy: a path match to allow or deny, or an egress allow list for network connections. Policy narrows every grant on the computer, and no grant widens policy.
let rule = Rule::new("tests-are-evidence").deny_write("/workspace/tests/**");
let added = project.policy().add(rule).await?;
println!("{}", added.describe());rule tests-are-evidence denies write /workspace/tests/** policy sha256 9a41…e6f2, narrows every grant on quickstart
Policy::describe
async fn describe(&self) -> chio::Result<String>
Describes the policy in force: its hash and the one it replaced, your rules, and the defaults every computer starts with.
println!("{}", project.policy().describe().await?);policy sha256 9e42…c5d1, was 3c7b…8010 rules tests-are-evidence deny write /workspace/tests/** base forbidden paths **/.env, **/.env.*, **/.ssh/** and 27 more compiled HushSpec 0.1.0, evaluated at admission
Policy::hash
async fn hash(&self) -> chio::Result<PolicyHash>
Returns the hash every receipt records as policy_hash. A process you build yourself passes it to the kernel so its receipts name the same policy.
println!("{}", project.policy().hash().await?);sha256:9e42…c5d1
Computer::node_key and the project stores
async fn node_key(&self, name: &str) -> chio::Result<NodeKey>; History::receipt_store(&self) -> ReceiptStore; fn revocation_store(&self) -> RevocationStore
Attach a process you build yourself to the project's kernel state: a serving key your kernel certifies, the receipt store history reads, and the revocation store every grant is checked against. Connect an Application builds an axum service this way.
let node_key = project.node_key("pricing-http").await?;
let receipts = project.history().receipt_store();
let revocations = project.revocation_store();
let layer = chio_tower::ChioLayer::builder(node_key, project.policy().hash().await?)
.receipt_store(receipts)
.revocation_store(revocations)
.build()?;Computer::service
fn service(&self, name: &str) -> ServiceBuilder; .tool(name, target).create().await -> chio::Result<Service>
Exposes project functions as named tools and publishes them over MCP and HTTP. Every caller presents a grant from your kernel.
let pricing = project
.service("pricing")
.tool("quote", "src/pricing.py:quote")
.tool("refund", "src/pricing.py:refund")
.create()
.await?;
let endpoint = pricing.publish(["mcp", "http"]).await?;
println!("{}", endpoint.describe());service pricing, from quickstart @ main 2f60a91 tools quote · refund mcp https://127.0.0.1:9440/pricing/mcp http https://127.0.0.1:9440/pricing/v1, OpenAPI at /openapi.json admits callers that hold a grant from your kernel
Service::grant
fn grant(&self, grant: ServiceGrant) -> chio::Result<Authority>
Grants a caller named tools on the service, with a call rate, a budget spent at each tool's price, and an expiry.
let access = pricing.grant(
ServiceGrant::to("did:chio:z6MkbY4…e1R")
.tool("quote")
.limit(100, "1h")
.budget(USD(2))
.expires_in("24h"),
)?;
println!("{}", access.describe());grant cap-01ab…77d1, signed by your kernel to did:chio:z6MkbY4…e1R allows pricing.quote limits 100 calls per hour budget USD 2.00, USD 0.01 per call expires in 24 hours
Service::require
async fn require(&self, approval: Approval) -> chio::Result<RuleRef>
Pauses each matching call until the approver signs for that exact request.
let rule = pricing.require(Approval::by_you().when("quote.percent > 50")).await?;
println!("{}", rule.describe());rule quote.percent > 50 waits for did:chio:z6Mkf3…Qp9 (you) binds one signature to one request
Service::settle
async fn settle(&self, terms: Settle) -> chio::Result<SettlementTerms>; Settle::tool(name).rail(r).chain(c).token(t).payee(p)
Declares how a tool's calls are paid: the rail, the chain, the token, and where the payee comes from. The terms go inside the governed intent, so an approval required by require covers the chain, payee, amount, and token along with the request. Approval-Bound Payments walks the flow.
use chio::Settle;
let terms = pricing
.settle(Settle::tool("refund").rail("eip3009").chain("eip155:8453").token("USDC").payee("refund.wallet"))
.await?;
println!("{}", terms.describe());settles refund over eip3009 on eip155:8453 in USDC, payee from refund.wallet binding chain, payee, amount, token contract, dispatch, capability, expiry
Service::price
fn price(&self, tool: &str, amount: Money) -> PriceBuilder; .economy(urn).split([Payee]).await -> chio::Result<ToolPricing>; Payee::to(beneficiary).bps(n).vest(cliff, end).cosigner(did)
Sets a tool's price per call, the economy it settles in, and the payee split, one Payee per beneficiary. Your kernel writes the terms into the tool manifest as ToolPricing and signs the manifest again. A split must sum to exactly 10,000 basis points; each share may carry a vesting window and the cosigners who must sign it. Revenue Splits covers vesting and settlement.
use chio::Payee;
let partner = Payee::to("did:chio:z6MkpA7…c2T")
.bps(3333)
.vest("2026-11-01", "2027-01-30")
.cosigner("did:chio:z6Mkf3…Qp9");
let priced = pricing
.price("quote", USD("0.01"))
.economy("urn:chio:economy:pricing")
.split([
Payee::to("did:chio:z6Mkf3…Qp9").bps(5000),
partner,
Payee::to("urn:chio:economy:pricing:reserve").bps(1667),
])
.await?;
println!("{}", priced.describe());price pricing.quote USD 0.01 per call, economy urn:chio:economy:pricing
split 5000 did:chio:z6Mkf3…Qp9
3333 did:chio:z6MkpA7…c2T, vests 2026-11-01 to 2027-01-30, cosigned by you
1667 urn:chio:economy:pricing:reserve
manifest re-signed, payee_split sums to 10000 bpsService::events
fn events(&self) -> impl Stream<Item = chio::Result<CallEvent>>
Streams calls to the service as your kernel decides them.
let mut calls = pricing.events();
while let Some(call) = calls.next().await {
println!("{}", call?.summary());
}00:00 z6MkbY4 quote sku-118 at 10% allow USD 0.01 00:04 z6MkbY4 refund order-77 refused not in the grant 00:05 z6MkQx8 quote sku-118 at 10% refused no grant from your kernel 00:09 z6MkbY4 quote sku-301 at 80% pending approval required 00:41 z6MkbY4 quote sku-301 at 80% allow USD 0.01
Service::usage
fn usage(&self) -> UsageQuery; .since(d).await -> chio::Result<Usage>
Summarizes each caller's calls, refusals, approvals, and spend, computed from that caller's signed receipts.
println!("{}", pricing.usage().since("1h").await?);caller calls refused approvals spent z6MkbY4…e1R 100 2 1 USD 1.00 z6MkQx8…7Ha 0 1 0 USD 0.00
Computer::treaty
async fn treaty(&self, terms: Treaty) -> chio::Result<TreatyRef>
Agrees terms with a partner: the actions you cooperate on and what each requires. Both kernels sign, and each computes the intersection of the two governance ladders, so the stricter requirement wins.
let treaty = project
.treaty(
Treaty::with(&partner)
.actions(["workspace.read", "workspace.write", "model.invoke", "merge"])
.expires_in("7d"),
)
.await?;
println!("{}", treaty.describe());treaty trt-01aa…4c19, signed by both kernels participants you · partner (did:chio:z6MkpA7…c2T) action you them admitted as workspace.read allow allow either kernel admits under its own policy workspace.write allow allow either kernel admits under its own policy model.invoke allow allow charged at your door merge co-sign co-sign both kernels sign expires in 7 days
TreatyRef::revoke
async fn revoke(&self) -> chio::Result<()>
Ends the treaty and every grant issued under it, including grants the partner's agents delegated to each other. The revocation reaches the partner's kernel over iroh, and its next call is refused.
treaty.revoke().await?;
let mut events = run2.events();
while let Some(event) = events.next().await {
println!("{}", event?.summary());
}00:00 them admitted "add banker's rounding as an option" under trt-01aa…4c19 00:04 you revoked trt-01aa…4c19, revocation root sent over iroh 00:05 them refused fixer-1 write /workspace/src/pricing.py: treaty revoked 00:05 them stopped their work closed, no effects after revocation
Computer::mailbox
fn mailbox(&self, name: &str) -> MailboxBuilder; .partner(&p).treaty(&t).open().await -> chio::Result<Mailbox>
Opens a mailbox shared with a partner under a treaty. Each message is sent and claimed under its sender's or reader's own grant.
let reviews = project.mailbox("reviews").partner(&partner).treaty(&treaty).open().await?;
println!("{}", reviews.describe());mailbox reviews, under trt-01aa…4c19 senders partner's agents, each under its own grant readers your agents that hold a reads grant
first_passing
async fn first_passing(runs: impl IntoIterator<Item = Run>, check: &str) -> chio::Result<Run>
Waits on several runs and returns the first whose sealed fork passes the check. The other runs are cancelled, and their receipts stay in history.
use chio::first_passing;
let ours = fixer.run("fix issue #414").on(&fork_a).authority(grant_a).start().await?;
let theirs = partner.exec("fix issue #414").on(&fork_b).authority(grant_b).start().await?;
let winner = first_passing([ours, theirs], "pytest").await?;
println!("{}", winner.describe());
project.apply(winner.changes()).await?;winner run-01ab…0c7d, partner on fork c4e81b7 check pytest: 14 passed others cancelled, receipts kept in history
Program composition
impl BitAnd for Program (parallel); impl BitOr for Program (sequence)
Composes boot programs, the pinned descriptors your project exports. & runs programs at once and | runs them in sequence; Rust binds & tighter, so the expression below needs no parentheses. Operators build immutable syntax and run nothing. A fork started with a boot program runs it under the fork's grant.
use programs::{patch, reproduce, survey, verify};
let fork = project.fork().boot(reproduce() & survey() | patch() | verify()).await?;
println!("{}", fork.describe());boot (reproduce & survey) | patch | verify fork a3d90f1 of quickstart, base main 2f60a91
USD
fn USD(dollars: u64) -> Money; Money::cents(cents: u64) -> Money
Constructs an exact dollar amount for budgets and prices. Money stores integer cents, so arithmetic is exact.
use chio::{Money, USD};
println!("{}", USD(2));
println!("{}", Money::cents(1) * 100);USD 2.00 USD 1.00
Error::ApplyConflict
chio::Error::ApplyConflict { base: Revision, current: Revision }
Apply refuses to publish when main moved after the fork was taken. Nothing changes; fork again from the new main and reapply.
match project.apply(fork.changes()).await {
Err(conflict @ chio::Error::ApplyConflict { .. }) => println!("{conflict}"),
result => {
result?;
}
}main moved from 7c2e1d0 to 9e03a4b; fork 51c07ae was not applied
How the Computer reaches the kernel
Each grant is a chio.capability.v1 token signed by your kernel. Each operation in a run is a mediated call, and each event carries the id of a chio.receipt.v1 receipt in the store that history reads. Policy rules compile into the HushSpec policy every grant on the computer is checked against. The Computer and the Kernel maps each Computer object onto its kernel mechanism. The low-level clients below speak to the same kernel and the same records without the Computer layer.
Work
WorkClient buys checked work from another company through your owner work service. You select a partner's offer, prepare the agreement both sides sign, submit the funded commitment, and read the work view until the verifier's decision settles the claim. Your owner service holds every key; the client sends chio.work.v1 documents and never constructs authority. Verifiable Work explains the model.
The client ships in chio-runtime behind its work feature, under chio_runtime::work. Calls that reach the owner service are async and return chio_runtime::work::Result; the document builders and describe are synchronous. WorkClient is generic over its WorkTransport, and open picks the authenticated local socket. Documents are typed values with private fields and checked constructors; builders produce the same bytes as the wire documents. The client never retries a side effect and never generates a replacement command id.
cargo add chio-runtime --features workThe examples follow Run Verifiable Work: work is the client on ./work, your partner sells an authentication inventory of the pricing service's OpenAPI document for USD 28.00, and the examples run inside an async main.
WorkClient::open
async fn WorkClient::open(state: impl AsRef<Path>) -> Result<WorkClient>
Connects to the owner work service serving this state directory, over its authenticated local socket. chio work serve must be running; opening dispatches nothing.
use chio_runtime::work::WorkClient;
let work = WorkClient::open("./work").await?;work.select
async fn work.select(offer: Signed<WorkOffer>, program_id: &str, command_id: &str) -> Result<WorkSelection>
Selects the exact offer you pass: your owner service signs the selection, the partner's kernel seals a dispatch permit for one slot, and the result's commitment() is a draft WorkCommitmentV1 with no funding attached.
use chio_runtime::work::{Signed, WorkOffer};
let offer = Signed::<WorkOffer>::from_file("partner-offer.json")?;
let draft = work.select(offer, "pricing-openapi-auth", "auth-select-1").await?;
draft.commitment().write_file("auth-draft.json")?;
println!("{}", draft.describe());offer openapi-explicit-auth-v1 from did:chio:z6MkpA7…c2T, USD 28.00 selection signed by did:chio:z6Mkf3…Qp9 at revision 2 permit sealed by kernel 9b07…d31c for slot auth-inventory draft WorkCommitmentV1 written to auth-draft.json, no funding yet
WorkPreparationV1::agreement
fn WorkPreparationV1::agreement(program_id: &str, command_id: &str, commitment: WorkCommitmentV1) -> WorkPreparationV1; fn funding_profile(self, id: &str) -> Self; fn terms(self, reference: &str) -> Self
Builds the Agreement proposal of a chio.work.preparation.v1 document: the draft commitment, the funding profile the rail is configured under, and the terms file both parties sign.
use chio_runtime::work::WorkPreparationV1;
let proposal =
WorkPreparationV1::agreement("pricing-openapi-auth", "auth-agree-1", draft.commitment())
.funding_profile("work-claim-escrow")
.terms("terms/openapi-auth.json");work.prepare
async fn work.prepare(proposal: WorkPreparationV1) -> Result<WorkPreparedV1>
Prepares one owner artifact from a chio.work.preparation.v1 document. The proposal is one of Delegate, Offer, Select, Extend, Join, or Agreement, and the outcome is Prepared, Pending, or Refused. An agreement binds the terms both parties sign and moves no money; chio work fund locks the allocation and attaches its WorkFundingRefV1 to the commitment.
let prepared = work.prepare(proposal).await?;
println!("{}", prepared.describe());preparation auth-agree-1 Prepared
agreement 7e1d…c04a SHA-256 of the RFC 8785 body
buyer did:chio:z6Mkf3…Qp9 signed
provider did:chio:z6MkpA7…c2T signed through kernel.halden.example/cosign
verifier verify.corbel.example pinned 0x9f62…3ad5
checker openapi-explicit-auth-v1 c7a1…02be
amount USD 28.00 to 0x7d0c…12f4
deadlines submitBy 2026-10-14T18:00Z challengeUntil 2026-10-15T18:00Z
resolveBy 2026-10-16T18:00Z refundAfter 2026-10-17T18:00ZWorkCommitmentV1::from_file
fn WorkCommitmentV1::from_file(path: impl AsRef<Path>) -> Result<WorkCommitmentV1>; fn write_file(&self, path: impl AsRef<Path>) -> Result<()>
Reads or writes a commitment as its JSON document. Reading decodes through the checked conversions, so a duplicate key, an unknown field, or an out-of-range number is refused; Signed::<WorkOffer>::from_file reads an offer the same way.
use chio_runtime::work::WorkCommitmentV1;
let commitment = WorkCommitmentV1::from_file("auth-commitment.json")?;WorkCommandV1::submit
fn WorkCommandV1::submit(program_id: &str, command_id: &str, commitment: WorkCommitmentV1) -> WorkCommandV1
Builds a chio.work.command.v1 document whose action is Submit with the funded commitment.
use chio_runtime::work::WorkCommandV1;
let command = WorkCommandV1::submit("pricing-openapi-auth", "auth-submit-1", commitment);work.submit
async fn work.submit(command: WorkCommandV1) -> Result<WorkCommandResultV1>
Submits one chio.work.command.v1 document under its caller-supplied command id. The action is one of Delegate, Select, Seal, Extend, Submit, Reconcile, or Cancel. The outcome is Applied with a revision, Pending with the owner's operation reference, or Refused. Applied reports that the command was processed, not that the work succeeded. The same command id with the same body returns the original outcome; a different body is refused before dispatch.
let submitted = work.submit(command).await?;
println!("{}", submitted.describe());command auth-submit-1 Applied at revision 4 admitted by kernel 9b07…d31c under its own policy
WorkCommandResultV1::handle
fn handle(&self) -> Result<WorkHandleV1>
The handle an applied Submit returns: program, slot, allocation hash, receiver kernel, and original request id. It is a reference, not a bearer permit. On a Pending or Refused outcome it returns WorkError; query the original command id instead.
println!("{}", serde_json::to_string_pretty(&submitted.handle()?)?);{
"program_id": "pricing-openapi-auth",
"slot_id": "auth-inventory",
"allocation_hash": "a51e…07c3",
"receiver_kernel_id": "9b07…d31c",
"request_id": "req-01c4…7a90"
}work.inspect
async fn work.inspect(handle: WorkHandleV1) -> Result<WorkViewV1>
Reads the work view for a handle. It is a query, so it mutates nothing and dispatches no checker.
println!("{}", work.inspect(submitted.handle()?).await?.describe());handle pricing-openapi-auth/auth-inventory, request req-01c4…7a90 execution Available returned, rcpt-01c4…7a92 signed by kernel 9b07…d31c acceptance Pending verify.corbel.example, recordable after 2026-10-15T18:00Z results Available output 6f0e…b31a, 3 operations recovery NotApplicable settlement Pending Submitted, commitment 0x2a90…f1e3 delivery Available co-signed by kernels 4f1c…a2e9 and 9b07…d31c
work.query
async fn work.query(query: WorkQueryV1) -> Result<WorkQueryResultV1>
Reads by reference: Work by handle, Command or Preparation by original id, and Catalog or Profile for the entries owners provisioned for you. An original-id query answers before you hold a handle, so it resolves a lost response.
use chio_runtime::work::WorkQueryV1;
let found = work.query(WorkQueryV1::command("auth-submit-1")).await?;
println!("{}", found.describe());query Command auth-submit-1 command auth-submit-1 Applied at revision 4 handle pricing-openapi-auth/auth-inventory, request req-01c4…7a90
work.collect
async fn work.collect(handle: WorkHandleV1) -> Result<WorkViewV1>
Reconciles finished work: it records the settlement and evidence the owning authorities already hold and returns the view. It never dispatches the work again and never returns protected result bytes; read those through the recovery release operation.
let view = work.collect(submitted.handle()?).await?;
println!("{}", view.describe());handle pricing-openapi-auth/auth-inventory, request req-01c4…7a90 execution Available returned, rcpt-01c4…7a92 signed by kernel 9b07…d31c acceptance Available Accepted, predicate match, checker c7a1…02be results Available output 6f0e…b31a, 3 operations recovery NotApplicable settlement Available Paid USD 28.00, allocation 0x8c1f…40d2 delivery Available co-signed by kernels 4f1c…a2e9 and 9b07…d31c
WorkCommitmentV1
fn handle(&self) -> &WorkHandleV1; fn permit(&self) -> &Signed<DispatchPermit>; fn profile_ref(&self) -> &WorkProfileRefV1; fn funding(&self) -> Option<&WorkFundingRefV1>
Links the handle, the sealed permit, the agreed profile, the custody reference for the exact invocation, and the graph. funding() names the rail, the agreement digest, and the reserve; the receiver checks the allocation on the rail before it admits the work.
let commitment = WorkCommitmentV1::from_file("auth-commitment.json")?;
println!("{}", serde_json::to_string_pretty(&commitment.funding())?);{
"rail": "work-claim-escrow",
"agreement_digest": "7e1d…c04a",
"reserve_ref": "0x8c1f…40d2"
}WorkViewV1
fn execution(&self) -> &Observation; fn acceptance(&self) -> &Observation; fn results(&self) -> &Observation; fn recovery(&self) -> &Observation; fn settlement(&self) -> &Observation; fn delivery(&self) -> &Observation; fn describe(&self) -> String
Six independent observations. Each is NotApplicable, Pending with the owner's reference, Available with its source, revision, time, and value, or Unavailable with a code. Each authority revises its own observation, so acceptance and settlement can move separately from execution.
println!("{}", serde_json::to_string_pretty(view.acceptance())?);{
"Available": {
"source": "verify.corbel.example",
"revision": 1,
"observed_at": "2026-10-15T18:04Z",
"value": { "decision": "Accepted", "predicate": "match", "checker": "c7a1…02be" }
}
}WorkError
pub struct WorkError; fn code(&self) -> &str; fn command_ref(&self) -> Option<&CommandRef>
A stable code and the original command reference. After a timeout, query that reference; the outcome the owner recorded is the answer.
match work.submit(command).await {
Ok(submitted) => println!("{}", submitted.describe()),
Err(_) => {
let found = work.query(WorkQueryV1::command("auth-submit-1")).await?;
println!("{}", found.describe());
}
}query Command auth-submit-1 command auth-submit-1 Applied at revision 4 handle pricing-openapi-auth/auth-inventory, request req-01c4…7a90
Low-level crates
The Computer API is built on crates you can use directly: verification helpers for evidence you receive, the guard SDK for policy extensions the kernel loads, and chio-tower for evaluation inside your own HTTP service.
Verify evidence without running a kernel
chio-binding-helpers exposes the same canonicalization and verification contract used across Chio’s language bindings. For a receipt, supply the signed JSON and an independently trusted kernel key:
// Inside a function returning chio_binding_helpers::Result<_>:
let verification = chio_binding_helpers::verify_receipt_json_with_trusted_signer_hex(
receipt_json,
&[trusted_kernel_key_hex],
)?;verification.ok checks integrity and signer trust. verification.authorized additionally requires an authorizing mediated decision. A valid denial receipt should verify successfully while remaining unauthorized. The complete example below handles both.
A standalone verifier
An auditor checks the receipts you export without running a kernel. Create a crate named receipt-check, add the helpers, and place the verification program in src/main.rs.
cargo new receipt-check && cd receipt-check
cargo add chio-binding-helpersSupply the exported receipt.json and your kernel key in trusted-kernel-key.hex, then run:
cargo runSuccess prints the recorded decision and whether authorization is established. Invalid integrity or an untrusted signer returns an error. The program opens no sockets.
Crate layout
The low-level SDK crates, each published under its own name, with the source path it is built from.
| Crate | Path | Role |
|---|---|---|
chio- | crates/ | Canonical JSON, SHA-256, Ed25519, and receipt, capability, and manifest verification. The contract every language binding implements. |
chio- | crates/ | A C ABI over chio- |
chio- | crates/ | A C ABI over chio-core-types and chio- |
chio- | crates/ | Reference verifier and exporter for chio. |
chio-guard-sdk | crates/ | Guest-side SDK for WASM guards targeting the chio:guard@0.2.0 WIT world: types, host bindings, ABI glue, and the guest allocator. |
chio- | crates/ | The chio_guard proc-macro attribute that wraps a guard author's evaluate function. |
Three crates outside that group appear on this page. crates/core/chio-core holds the kernel types (ChioReceipt, CapabilityToken, ChioScope) and validate_delegation_chain, which the invariants wrap. crates/platform/chio-manifest holds the signed tool manifest types. crates/protocol/chio-tower governs an HTTP service in process.
chio-binding-helpers
The crate exposes seven modules and re-exports their public names at the root (src/lib.rs:22-40). It implements RFC 8785 canonical JSON, SHA-256, Ed25519 sign and verify, receipt parsing and verification, capability parsing and verification including delegation-chain walking through chio-core, and signed manifest parsing and verification. Session runtime, transport, authentication, and callback orchestration stay in the language-native SDKs (src/lib.rs:1-12).
| Module | Re-exported at the root |
|---|---|
canonical | canonicalize_ (canonical.rs:6). |
hashing | sha256_ (hashing.rs:2) and sha256_hex_utf8 (:7). |
signing | public_ at line 23, is_ at 28, is_ at 33, sign_ at 37, verify_ at 46, sign_ at 56, verify_ at 70, plus Utf8 and Canonical. |
receipt | parse_ at line 71, receipt_ at 75, verify_receipt at 79, verify_ at 83, verify_ at 120, verify_ at 128, verify_ at 133, plus Receipt and Receipt. |
capability | parse_ at line 25, capability_ at 29, verify_ at 33, verify_ at 57, plus Capability and Capability. |
manifest | parse_ at line 15, signed_ at 19, verify_ at 23, verify_ at 39, plus Manifest. |
error | Error, ErrorCode, and the Result alias. |
Receipt verification
ReceiptVerification (receipt.rs:56-68) carries signature_valid, parameter_hash_valid, receipt_id_valid, decision, receipt_kind, boundary_class, trust_level, result, authorized, signer_key_hex, signer_trusted, and ok. signer_trusted is true only when the caller supplies a signer list that contains the receipt's kernel_key, and both authorized and ok conjoin it (receipt.rs:95-117). verify_receipt passes an empty list (receipt.rs:80), so it reports the three validity booleans and leaves authorized false. Pass keys through the trusted-signer forms to get an authorization answer.
use chio_binding_helpers::verify_receipt_json_with_trusted_signer_hex;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let json = std::fs::read_to_string("receipt.json")?;
// Supplied independently by your trusted operator configuration.
let trusted_key = std::fs::read_to_string("trusted-kernel-key.hex")?;
let verification =
verify_receipt_json_with_trusted_signer_hex(&json, &[trusted_key.trim()])?;
if !verification.ok {
return Err("receipt integrity or signer trust check failed".into());
}
println!("Recorded decision: {:?}", verification.decision);
println!("Authorization established: {}", verification.authorized);
Ok(())
}Never copy the receipt’s own kernel_key into the trusted signer list. The display field result describes recorded semantics; it can say Authorized even when verification fails. Branch on the verification booleans and match the receipt to the expected request.
| Field | Meaning |
|---|---|
signature_valid, parameter_, receipt_ | The signed record and its content- |
signer_trusted | The signer is in the independently supplied allow-list. |
ok | All three integrity checks and signer trust pass. A denial can satisfy this. |
authorized | The verified, trusted record also establishes an allow decision at the mediated prevention boundary. |
Capability verification
verify_capability takes the parsed token, a u64 Unix timestamp, and an Option<u32> maximum delegation depth (capability.rs:33-37). The returned CapabilityVerification carries signature_valid, delegation_chain_shape_valid, time_valid, and time_status, whose variants are Valid, NotYetValid, and Expired (capability.rs:11-15, with the struct at :18-23).
use chio_binding_helpers::{parse_capability_json, verify_capability, Result};
fn inspect_capability(json: &str, now: u64) -> Result<bool> {
let capability = parse_capability_json(json)?;
let status = verify_capability(&capability, now, Some(8))?;
println!("Time status: {:?}", status.time_status);
Ok(status.signature_valid
&& status.delegation_chain_shape_valid
&& status.time_valid)
}This is an offline structural check. It does not establish issuer trust, current revocation, request scope, proof of possession, or complete delegation attenuation. The compatibility chain checker verifies signatures, adjacent identities, timestamps, and depth; it does not enforce parent_scope_hash binding. Use the kernel’s request-validation path for an authorization decision.
Manifest verification
ManifestVerification (manifest.rs:8-13) carries structure_valid, signature_valid, embedded_public_key_valid, and embedded_public_key_matches_signer.
use chio_binding_helpers::{
parse_signed_manifest_json,
verify_signed_manifest,
};
let manifest = parse_signed_manifest_json(&json)?;
let result = verify_signed_manifest(&manifest)?;
assert!(result.structure_valid);
assert!(result.signature_valid);
assert!(result.embedded_public_key_valid);
assert!(result.embedded_public_key_matches_signer);These booleans validate the manifest and its embedded signing relationship. Your application still chooses which manifest signer to trust.
Ed25519 and canonical JSON
sign_json_str_ed25519 canonicalizes the input and returns a CanonicalJsonSignature whose fields are canonical_json, public_key_hex, and signature_hex (signing.rs:12-16). verify_json_str_signature_ed25519 takes the input, then the public key, then the signature.
use chio_binding_helpers::{
canonicalize_json_str,
sha256_hex_bytes,
sha256_hex_utf8,
sign_json_str_ed25519,
verify_json_str_signature_ed25519,
sign_utf8_message_ed25519,
verify_utf8_message_ed25519,
};
let canonical = canonicalize_json_str(r#"{"b":2,"a":1}"#)?;
assert_eq!(canonical, r#"{"a":1,"b":2}"#);
let digest = sha256_hex_utf8("hello world");
let sig = sign_json_str_ed25519(&canonical, &seed_hex)?;
let ok = verify_json_str_signature_ed25519(&canonical, &sig.public_key_hex, &sig.signature_hex)?;
assert!(ok);chio-guard-sdk
crates/sdk/chio-guard-sdk is the guest-side SDK for writing WASM guards against the chio:guard@0.2.0 WIT world. It compiles to wasm32-unknown-unknown for production guards, and on native targets it compiles with no-op fallbacks for the host imports so cargo test runs without a WASM runtime (src/lib.rs:18-20).
| Module | What it holds |
|---|---|
types | GuardRequest, GuardVerdict, and Guest, with serde annotations matching the host ABI, plus the constants VERDICT_ALLOW and VERDICT_DENY. |
host | Wrappers for the host imports chio.log, chio.get_config, chio., and chio:guard/, exported as log, log_level, get_config, get_time, and fetch_blob, plus the PolicyContext bundle-handle wrapper. |
glue | read_request deserializes from linear memory, encode_verdict produces the ABI return code, and the crate exports chio_ for structured deny reasons. |
alloc | The chio_alloc and chio_free exports the host runtime probes for allocation in guest linear memory. |
The four module declarations sit at src/lib.rs:38-41 and the root re-exports at :44-46. chio_guard_sdk::prelude (:52-55) re-exports the glue functions, the host bindings, and the types other than GuestDenyResponse.
use chio_guard_sdk::prelude::*;
fn evaluate(req: GuardRequest) -> GuardVerdict {
if req.tool_name == "dangerous_tool" {
GuardVerdict::deny("tool is blocked by policy")
} else {
GuardVerdict::allow()
}
}crates/sdk/chio-guard-sdk-macros is a proc-macro crate whose one export is the chio_guard attribute (src/lib.rs:159-160), which wires an author's evaluate function to the exports above. sdks/rust/chio-guard-sdk-compat is a compatibility package that re-exports the same API under the library name chio_guard_sdk (sdks/README.md:18). It declares an empty [workspace] table, so it stands outside the root workspace and builds through --manifest-path.
The C ABI crates
Two crates carry a C ABI. Both declare crate-type = ["staticlib", "cdylib", "rlib"] so a C or C++ consumer links either form.
chio-bindings-ffiexports the invariants over a C boundary: UTF-8 strings and byte buffers in, UTF-8 buffers out, Rust-side deallocation, and no async or session state crossing the boundary (src/lib.rs:1-5). It declaresCHIO_FFI_ABI_VERSIONand theCHIO_FFI_STATUS_*constants atsrc/lib.rs:14-20. The Bindings API page lists its exported symbols.chio-cpp-kernel-ffibindschio-core-typesandchio-kernel-corefor the C++ offline kernel package. It mirrors the mobile adapter's JSON-in, JSON-out shape over a plain C ABI, so no UniFFI or Rust concept appears in a public C++ header (src/lib.rs:1-5).
chio-eval-receipt
crates/sdk/chio-eval-receipt verifies eval-report receipt bundles end to end: it validates the bundle envelope, recomputes the corpus SHA-256, and checks the detached memo signatures attached to each receipt, failing closed on any mismatch (src/lib.rs:3-7). It also exports scenario runs into the bundle format, and it ships a chio-eval-receipt binary at src/bin/cli.rs.
The two entry points are verify_bundle with verify_fixture_bundle, which parse a bundle JSON document into a VerifiedBundle, and export_scenario_run, which builds a Bundle from scenario inputs (src/lib.rs:9-11). The crate pins the schema it verifies as constants: BUNDLE_SCHEMA_ID is chio.eval-report.bundle.v1 and BUNDLE_SCHEMA_PATH is spec/eval/receipt-format.v1.json (src/lib.rs:34 and :37). It carries #![forbid(unsafe_code)] at src/lib.rs:13.
Embed evaluation in an HTTP service
chio-tower brings Chio evaluation into a Rust service process. It wraps compatible Tower HTTP services. With durable stores configured, it records authorization evidence before invoking the inner handler, then records the resulting HTTP receipt. This supports policy-aware service boundaries without a network hop to a sidecar.
Supply the host’s durable stores
The durable integration uses an evaluator with receipt and revocation stores, qualified admission and outcome stores, and their mutation fence. The following is the assembly shape once those stores have been provisioned:
use chio_tower::{ChioEvaluator, ChioLayer};
let evaluator = ChioEvaluator::builder(keypair, policy_hash)
.receipt_store(receipt_store)
.revocation_store(revocation_store)
.durable_admission_stores(admission_store, outcome_store, mutation_fence)
.build()?;
let layer = ChioLayer::from_evaluator(evaluator);The evaluator integration tests show store provisioning and the authority-lock directory. ChioLayer::builder does not expose the qualified admission-store method; use from_evaluator for that setup. ChioLayer::new starts without durable stores and fails closed; new_ephemeral is an explicit local scaffold.
Check the service contract
| Integration concern | Current contract |
|---|---|
Request and response bodies | Request bodies implement Body + From<Bytes>; response bodies implement Default + From<Bytes>, with Send and static lifetimes. The default buffered-body limit is 8 MiB. |
Async runtime | Use a multi-thread Tokio runtime for the synchronous bridge into async kernel dispatch, as exercised by the HTTP integration tests. |
Axum error handling | Adapt the layer’s boxed errors to Axum’s Infallible service contract. The source includes an explicit adapter in its Axum integration test. |
Identity | The default header extractor marks identities unverified. Extracting a bearer token, API key, or cookie is not authenticating it. |
Policy | The current evaluator selects SessionAllow for safe methods and DenyByDefault otherwise. The policy_hash parameter records context; it does not load policy rules. |
Handler effects | Evidence recording around a handler does not make its external effects transactional. The host still owns execution and recovery. |
For remote MCP sessions or receipt-query pagination, use the TypeScript, Python, or Go client surfaces where supported, or the documented protocol endpoints. chio-binding-helpers itself opens no sockets.
Error model
chio_binding_helpers::Error (error.rs:37-46) is a thiserror enum with three variants: Core, Json, and Manifest. Each is produced only by #[from] conversion from the underlying crate's error, so there is no Error::new constructor: parse and verify helpers propagate with ?. Call .code() (error.rs:50) to map an Error onto an ErrorCode, the serde snake-case enum at error.rs:5-34 that the other bindings share.
use chio_binding_helpers::{parse_capability_json, verify_capability};
fn inspect(json: &str, now: u64) {
let result = parse_capability_json(json)
.and_then(|cap| verify_capability(&cap, now, Some(8)));
match result {
Err(error) => eprintln!("Verification could not complete: {:?}", error.code()),
Ok(status) => {
println!("Signature: {}", status.signature_valid);
println!("Chain shape: {}", status.delegation_chain_shape_valid);
println!("Time: {:?}", status.time_status);
}
}
}An expired token normally returns a verification result with time_valid = false. A broken chain can return delegation_chain_shape_valid = false. Handle these result fields as well as Err.
Source
The public SDK crates define the verification helpers, C ABIs, guard SDK, and evaluation-receipt tooling. The crate table renders checked workspace manifests. The compatibility shim is sdks/rust/chio-guard-sdk-compat; HTTP integration lives in crates/protocol/chio-tower.
The chio crate is the Computer API; the installer's chio binary is the CLI built from chio-cli. Below the Computer layer, import the crate for the boundary you need: chio-binding-helpers for local invariants, chio-core and chio-core-types for shared types, and chio-kernel-core for the portable decision core.
Conformance
The Rust crates are the reference the other languages are checked against. One JSON corpus of vectors covers canonical JSON, hashing, signing, receipts, capabilities, and manifests, and chio-binding-helpers round-trips every vector through its public API. The installed CLI runs the same corpus against each peer language:
chio conformance run --peer allRelated
verify_receipt reads.HTTP substratethe sidecar endpoints a Rust service calls when it needs more than verification.