C++ SDK
Run agents on a computer from C++, with native sessions and offline receipt verification beneath it.
The chio package carries the Computer API from the Quickstart, a native session client, and the invariants for canonical JSON and signature verification. Every call returns chio::Result<T>; nothing throws.
0 entries. Choose a section or search the full text.
Install
vcpkg add port chioLink with CMake
cmake_minimum_required(VERSION 3.16)
project(Pricing LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(ChioCpp CONFIG REQUIRED)
add_executable(pricing main.cpp)
target_link_libraries(pricing PRIVATE ChioCpp::chio_cpp)Configure with the vcpkg toolchain file, as for any vcpkg port. The package links the Rust invariants library it needs; your build needs no Rust toolchain.
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 are synchronous and return chio::Result<T>, the same result type the rest of the SDK uses. CHIO_TRY(decl, expr) from chio/result.hpp binds the value or returns the error from the enclosing function, and CHIO_TRY_VOID(expr) does the same for Result<void>, so the fragments below run inside a function that returns chio::Result<void>. Methods with options return builders. Event ranges yield chio::Result<RunEvent>. The SDK targets C++17.
This program is Quickstart part 1 in one file. It runs inside the computer that chio new quickstart creates.
#include <chio/computer.hpp>
#include <iostream>
chio::Result<void> quickstart() {
CHIO_TRY(auto project, chio::Computer::open("quickstart"));
CHIO_TRY(auto fixer, project.agent("fixer"));
CHIO_TRY(auto fork, project.fork());
CHIO_TRY(auto authority, fork.grant(chio::Grant::to(fixer)
.resource("/workspace", {"read", "write"})
.resource("/models/default", {"invoke"})
.budget(chio::USD(2))
.expires_in(std::chrono::minutes(20))));
CHIO_TRY(auto run, fixer.run("fix issue #412").on(fork).authority(authority).start());
for (auto next : run.events()) {
CHIO_TRY(auto event, next);
std::cout << event.summary() << '\n';
}
CHIO_TRY(auto refusals, project.history().receipts().outcome(chio::Outcome::Deny).all());
std::cout << refusals.front().describe() << '\n';
return chio::Result<void>::success();
}
int main() {
auto done = quickstart();
if (!done) {
std::cerr << done.error().message << '\n';
return 1;
}
}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.
chio::Computer::open
static Result<Computer> Computer::open(std::string_view name)
Opens a computer your operator identity owns and returns its handle. The handle reads the computer's current revision; nothing is copied.
#include <chio/computer.hpp>
CHIO_TRY(auto project, chio::Computer::open("quickstart"));
std::cout << project.describe() << '\n';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 fileschio::Computer::connect
static Result<Partner> Computer::connect(std::string_view name)
Resolves a paired partner by the name you gave its card, and authenticates its kernel before returning.
CHIO_TRY(auto partner, chio::Computer::connect("partner"));
std::cout << partner.describe() << '\n';partner partner, did:chio:z6MkpA7…c2T operator Halden, halden.example kernel 9b07…d31c, pinned from its card transport iroh, authenticated at handshake
chio::Computer::partners
static Result<std::vector<Partner>> Computer::partners()
Lists every partner you have paired with, each with its pinned kernel key and the treaty in force.
CHIO_TRY(auto partners, chio::Computer::partners());
for (const auto& partner : partners) std::cout << partner.summary() << '\n';partner did:chio:z6MkpA7…c2T Halden kernel 9b07…d31c treaty trt-01aa…4c19
Computer::agent
Result<Agent> agent(std::string_view name) const
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.
CHIO_TRY(auto fixer, project.agent("fixer"));
std::cout << fixer.describe() << '\n';agent fixer, did:chio:z6MkrT2…8wd issued by your kernel 4f1c…a2e9 grants none
Computer::agents
AgentsBuilder agents(std::string_view role) const; .count(n).reads(mailbox).start() -> Result<std::vector<Agent>>
Starts several agents of one role, numbered from 1, optionally reading a shared mailbox.
CHIO_TRY(auto reviewers, project.agents("review").count(2).reads(reviews).start());
for (const auto& reviewer : reviewers) std::cout << reviewer.summary() << '\n';reviewer-1 did:chio:z6MkvR1…3mc reads reviews reviewer-2 did:chio:z6MkvR2…9tf reads reviews
Computer::fork
Result<Fork> fork(std::optional<Program> boot = std::nullopt) const
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.
CHIO_TRY(auto fork, project.fork());
std::cout << fork.describe() << '\n';fork 51c07ae of quickstart, base main 7c2e1d0 writes stay in the fork until you apply them
Fork::grant
Result<Authority> grant(const Grant& grant) const
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.
CHIO_TRY(auto authority, fork.grant(chio::Grant::to(fixer)
.resource("/workspace", {"read", "write"})
.resource("/models/default", {"invoke"})
.budget(chio::USD(2))
.expires_in(std::chrono::minutes(20))));
std::cout << authority.describe() << '\n';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
Result<Authority> reissue(const Fork& on) const
Issues a grant with the same terms on another fork. The original grant is unchanged.
CHIO_TRY(auto next, project.fork());
CHIO_TRY(auto again, authority.reissue(next));
std::cout << again.describe() << '\n';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 minutesAgent::run
RunBuilder run(std::string_view task) const; .on(fork).authority(a).start() -> Result<Run>
Sends a task to an agent under a grant. Every operation the agent attempts reaches your kernel before it reaches a resource.
CHIO_TRY(auto run, fixer.run("fix issue #412").on(fork).authority(authority).start());
std::cout << run.describe() << '\n';run run-01a8…e7c0 agent fixer, on fork 51c07ae authority cap-01a8…31f2
Run::events
EventRange events(); each element is 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.
for (auto next : run.events()) {
CHIO_TRY(auto event, next);
std::cout << event.summary() << '\n';
}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
ExecBuilder exec(std::string_view task) const; .on(fork).authority(a).report_to(mailbox).start() -> 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.
CHIO_TRY(auto run, partner.exec("fix issue #414").on(fork).authority(authority).report_to(reviews).start());
for (auto next : run.events()) {
CHIO_TRY(auto event, next);
std::cout << event.summary() << '\n';
}
CHIO_TRY_VOID(run.join_tree());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
Result<std::string> diff() const
Returns the changes the fork holds against its base, as a unified diff.
CHIO_TRY(auto diff, fork.diff());
std::cout << diff << '\n';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
ApplyBuilder apply(const ChangeSet& changes) const; .treaty(t).run() -> 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.
CHIO_TRY(auto merged, project.apply(fork.changes()).run());
std::cout << merged.describe() << '\n';applied fork 51c07ae onto quickstart main revision main 8d4b2e7, base 7c2e1d0 receipt rcpt-01a8…d51e, signed by your kernel
History::receipts
ReceiptQuery receipts() const; .outcome(o).agent(a).since(d); .first() or .all()
Reads signed receipts from the computer's history, filtered by outcome, agent, or time.
CHIO_TRY(auto refusal, project.history().receipts().outcome(chio::Outcome::Deny).first());
std::cout << refusal.describe() << '\n';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
History::export_to
Result<Evidence> export_to(const std::filesystem::path& dir) const
Writes the computer's history to a directory as an evidence package. Verification checks every signature against pinned kernel keys, offline. The method is export_to because export is a C++ keyword.
CHIO_TRY(auto evidence, project.history().export_to("audit"));
CHIO_TRY(auto verification, evidence.verify());
std::cout << verification << '\n';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
Policy::add
Result<RuleRef> add(const Rule& rule) const
Adds a named rule to the computer's policy. Policy narrows every grant on the computer, and no grant widens policy.
CHIO_TRY(auto rule, project.policy().add(
chio::Rule("tests-are-evidence").deny_write("/workspace/tests/**")));
std::cout << rule.describe() << '\n';rule tests-are-evidence denies write /workspace/tests/** policy sha256 9a41…e6f2, narrows every grant on quickstart
Computer::service
ServiceBuilder service(std::string_view name) const; .tool(name, target).create() -> Result<Service>
Exposes project functions as named tools and publishes them over MCP and HTTP. Every caller presents a grant from your kernel.
CHIO_TRY(auto pricing, project.service("pricing")
.tool("quote", "src/pricing.py:quote")
.tool("refund", "src/pricing.py:refund")
.create());
CHIO_TRY(auto endpoint, pricing.publish({chio::Protocol::Mcp, chio::Protocol::Http}));
std::cout << endpoint.describe() << '\n';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
Result<Authority> grant(const ServiceGrant& grant) const
Grants a caller named tools on the service, with a call rate, a budget spent at each tool's price, and an expiry.
CHIO_TRY(auto access, pricing.grant(chio::ServiceGrant::to("did:chio:z6MkbY4…e1R")
.tool("quote")
.limit(100, std::chrono::hours(1))
.budget(chio::USD(2))
.expires_in(std::chrono::hours(24))));
std::cout << access.describe() << '\n';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
Result<RuleRef> require(const Approval& approval) const
Pauses each matching call until the approver signs for that exact request.
CHIO_TRY(auto rule, pricing.require(chio::Approval::by_you().when("quote.percent > 50")));
std::cout << rule.describe() << '\n';rule quote.percent > 50 waits for did:chio:z6Mkf3…Qp9 (you) binds one signature to one request
Service::events
CallRange events() const; each element is Result<CallEvent>
Streams calls to the service as your kernel decides them.
for (auto next : pricing.events()) {
CHIO_TRY(auto call, next);
std::cout << call.summary() << '\n';
}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
Result<Usage> usage(std::chrono::seconds since) const
Summarizes each caller's calls, refusals, approvals, and spend, computed from that caller's signed receipts.
CHIO_TRY(auto usage, pricing.usage(std::chrono::hours(1)));
std::cout << usage << '\n';caller calls refused approvals spent z6MkbY4…e1R 100 2 1 USD 1.00 z6MkQx8…7Ha 0 1 0 USD 0.00
Computer::treaty
Result<TreatyRef> treaty(const Treaty& terms) const
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.
CHIO_TRY(auto treaty, project.treaty(
chio::Treaty::with(partner)
.actions({"workspace.read", "workspace.write", "model.invoke", "merge"})
.expires_in(std::chrono::hours(24 * 7))));
std::cout << treaty.describe() << '\n';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
Result<void> revoke() const
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.
CHIO_TRY_VOID(treaty.revoke());
for (auto next : run2.events()) {
CHIO_TRY(auto event, next);
std::cout << event.summary() << '\n';
}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
MailboxBuilder mailbox(std::string_view name) const; .partner(p).treaty(t).open() -> 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.
CHIO_TRY(auto reviews, project.mailbox("reviews").partner(partner).treaty(treaty).open());
std::cout << reviews.describe() << '\n';mailbox reviews, under trt-01aa…4c19 senders partner's agents, each under its own grant readers your agents that hold a reads grant
chio::first_passing
Result<Run> first_passing(std::vector<Run> runs, std::string_view check)
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.
CHIO_TRY(auto ours, fixer.run("fix issue #414").on(fork_a).authority(grant_a).start());
CHIO_TRY(auto theirs, partner.exec("fix issue #414").on(fork_b).authority(grant_b).start());
CHIO_TRY(auto winner, chio::first_passing({ours, theirs}, "pytest"));
std::cout << winner.describe() << '\n';
CHIO_TRY_VOID(project.apply(winner.changes()).run());winner run-01ab…0c7d, partner on fork c4e81b7 check pytest: 14 passed others cancelled, receipts kept in history
Program composition
Program operator&(Program, Program); Program operator|(Program, Program)
Composes boot programs, the pinned descriptors your project exports. & runs programs at once and | runs them in sequence; C++ binds & tighter, so the expression below needs no parentheses. A fork started with a boot program runs it under the fork's grant.
auto boot = programs::reproduce() & programs::survey() | programs::patch() | programs::verify();
CHIO_TRY(auto fork, project.fork(boot));
std::cout << fork.describe() << '\n';boot (reproduce & survey) | patch | verify fork a3d90f1 of quickstart, base main 2f60a91
chio::USD
Money USD(std::int64_t dollars); Money Money::cents(std::int64_t cents)
Constructs an exact dollar amount for budgets and prices. Money stores integer cents, so arithmetic is exact.
std::cout << chio::USD(2) << '\n';
std::cout << chio::Money::cents(1) * 100 << '\n';USD 2.00 USD 1.00
ErrorCode::ApplyConflict
chio::ErrorCode::ApplyConflict, with Error::message naming both revisions
Apply refuses to publish when main moved after the fork was taken. Nothing changes; fork again from the new main and reapply.
auto merged = project.apply(fork.changes()).run();
if (!merged && merged.error().code == chio::ErrorCode::ApplyConflict) {
std::cout << merged.error().message << '\n';
}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.
Invariants
The invariants run in process with no service. Canonical JSON follows RFC 8785, and every verification helper takes trusted keys from your configuration, never from the record being checked.
#include "chio/chio.hpp"
#include <iostream>
int main() {
auto result = chio::invariants::canonicalize_json(R"({"sku":"sku-118","percent":10})");
if (!result) return 1;
std::cout << result.value() << std::endl;
}Expected: {"percent":10,"sku":"sku-118"}.
Low-level session client
Beneath the Computer API, chio::Client opens MCP sessions on a kernel edge, such as the pricing service another operator publishes. It sends requests through an HttpTransport you supply, or through the bundled libcurl transport when the package is built with CHIO_CPP_ENABLE_CURL=ON.
C++ HTTP services put their routes under kernel policy with the Drogon middleware; see the Go and C++ worked applications.