Chio/Docs
LOGIN · JOIN

ReferenceSDKs

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.

Install

bash
vcpkg add port chio

Link with CMake

CMakeLists.txtcmake
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.

main.cppcpp
#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;
    }
}
Output
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.

cpp
#include <chio/computer.hpp>

CHIO_TRY(auto project, chio::Computer::open("quickstart"));
std::cout << project.describe() << '\n';
Output
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 files

chio::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.

cpp
CHIO_TRY(auto partner, chio::Computer::connect("partner"));
std::cout << partner.describe() << '\n';
Output
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.

cpp
CHIO_TRY(auto partners, chio::Computer::partners());
for (const auto& partner : partners) std::cout << partner.summary() << '\n';
Output
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.

cpp
CHIO_TRY(auto fixer, project.agent("fixer"));
std::cout << fixer.describe() << '\n';
Output
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.

cpp
CHIO_TRY(auto reviewers, project.agents("review").count(2).reads(reviews).start());
for (const auto& reviewer : reviewers) std::cout << reviewer.summary() << '\n';
Output
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.

cpp
CHIO_TRY(auto fork, project.fork());
std::cout << fork.describe() << '\n';
Output
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.

cpp
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';
Output
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 minutes

Authority::reissue

Result<Authority> reissue(const Fork& on) const

Issues a grant with the same terms on another fork. The original grant is unchanged.

cpp
CHIO_TRY(auto next, project.fork());
CHIO_TRY(auto again, authority.reissue(next));
std::cout << again.describe() << '\n';
Output
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 minutes

Agent::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.

cpp
CHIO_TRY(auto run, fixer.run("fix issue #412").on(fork).authority(authority).start());
std::cout << run.describe() << '\n';
Output
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.

cpp
for (auto next : run.events()) {
    CHIO_TRY(auto event, next);
    std::cout << event.summary() << '\n';
}
Output
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.

cpp
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());
Output
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.

cpp
CHIO_TRY(auto diff, fork.diff());
std::cout << diff << '\n';
Output
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.

cpp
CHIO_TRY(auto merged, project.apply(fork.changes()).run());
std::cout << merged.describe() << '\n';
Output
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.

cpp
CHIO_TRY(auto refusal, project.history().receipts().outcome(chio::Outcome::Deny).first());
std::cout << refusal.describe() << '\n';
Output
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.

cpp
CHIO_TRY(auto evidence, project.history().export_to("audit"));
CHIO_TRY(auto verification, evidence.verify());
std::cout << verification << '\n';
Output
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.

cpp
CHIO_TRY(auto rule, project.policy().add(
    chio::Rule("tests-are-evidence").deny_write("/workspace/tests/**")));
std::cout << rule.describe() << '\n';
Output
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.

cpp
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';
Output
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.

cpp
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';
Output
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.

cpp
CHIO_TRY(auto rule, pricing.require(chio::Approval::by_you().when("quote.percent > 50")));
std::cout << rule.describe() << '\n';
Output
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.

cpp
for (auto next : pricing.events()) {
    CHIO_TRY(auto call, next);
    std::cout << call.summary() << '\n';
}
Output
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.

cpp
CHIO_TRY(auto usage, pricing.usage(std::chrono::hours(1)));
std::cout << usage << '\n';
Output
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.

cpp
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';
Output
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.

cpp
CHIO_TRY_VOID(treaty.revoke());
for (auto next : run2.events()) {
    CHIO_TRY(auto event, next);
    std::cout << event.summary() << '\n';
}
Output
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.

cpp
CHIO_TRY(auto reviews, project.mailbox("reviews").partner(partner).treaty(treaty).open());
std::cout << reviews.describe() << '\n';
Output
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.

cpp
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());
Output
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.

cpp
auto boot = programs::reproduce() & programs::survey() | programs::patch() | programs::verify();
CHIO_TRY(auto fork, project.fork(boot));
std::cout << fork.describe() << '\n';
Output
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.

cpp
std::cout << chio::USD(2) << '\n';
std::cout << chio::Money::cents(1) * 100 << '\n';
Output
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.

cpp
auto merged = project.apply(fork.changes()).run();
if (!merged && merged.error().code == chio::ErrorCode::ApplyConflict) {
    std::cout << merged.error().message << '\n';
}
Output
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.

main.cppcpp
#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.