Chio/Docs
LOGIN · JOIN

ReferenceSDKs

TypeScript SDK

Find the TypeScript method, input, result, and error contract for the Computer API, hosted sessions, local verification, and cognition markets.

@chio-protocol/sdk carries the Computer API from the Quickstart and the protocol-level clients beneath it. Framework interception and browser execution use separate packages with different runtime boundaries.

TaskAPI / packageExecution boundary
Run agents on a computer
Your kernel; grants, runs, receipts, and apply.
Buy checked work from a partner
Your owner work service; selection, agreement, submit, and the work view.
Call hosted tools
Remote MCP edge; bearer admission and a configured tool catalog.
Verify a receipt
Local Node.js cryptography; caller supplies trusted signer keys.
Buy or sell verified findings
Market transport and configured settlement services.
Protect Express, Fastify, or Elysia
Colocated HTTP sidecar; handler runs after an accepted verdict.
Evaluate in a browser or portable host
Embedded decision core with host-provided state and effects.

Install

bash
npm install @chio-protocol/sdk

The package is ESM, uses node:crypto, and runs on Node.js >=22. A CommonJS application loads it with dynamic import(). Use the platform packages below when the host does not provide Node’s runtime APIs.

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.

Every call that reaches the kernel returns a Promise. grant, reissue, describe, and USD are synchronous: your local kernel issues a grant durably before grant returns. Event streams are async iterables. The package is ESM and runs on Node.js 22 or newer.

This program is Quickstart part 1 in one file. It runs inside the computer that chio new quickstart creates.

quickstart.tstypescript
import { Computer, USD } from "@chio-protocol/sdk";

const project = await Computer.open("quickstart");
const fixer = await project.agent("fixer");
const fork = await project.fork();
const authority = fork.grant({
  to: fixer,
  resources: { "/workspace": ["read", "write"], "/models/default": ["invoke"] },
  budget: USD(2),
  expiresIn: "20m",
});

const run = await fixer.run("fix issue #412", { on: fork, authority });
for await (const event of run.events()) console.log(event.summary);

const [refusal] = await project.history.receipts({ outcome: "deny" });
console.log(refusal.describe());
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.

Computer.open

Computer.open(name: string): Promise<Computer>

Opens a computer your operator identity owns and returns its handle. The handle reads the computer's current revision; nothing is copied.

typescript
import { Computer } from "@chio-protocol/sdk";

const project = await Computer.open("quickstart");
console.log(project.describe());
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

Computer.connect

Computer.connect(name: string): Promise<Partner>

Resolves a paired partner by the name you gave its card, and authenticates its kernel before returning.

typescript
const partner = await Computer.connect("partner");
console.log(partner.describe());
Output
partner    partner, did:chio:z6MkpA7…c2T
operator   Halden, halden.example
kernel     9b07…d31c, pinned from its card
transport  iroh, authenticated at handshake

Computer.partners

Computer.partners(): Promise<Partner[]>

Lists every partner you have paired with, each with its pinned kernel key and the treaty in force.

typescript
for (const partner of await Computer.partners()) console.log(partner.summary);
Output
partner    did:chio:z6MkpA7…c2T   Halden   kernel 9b07…d31c   treaty trt-01aa…4c19

computer.agent

computer.agent(name: string): Promise<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.

typescript
const fixer = await project.agent("fixer");
console.log(fixer.describe());
Output
agent      fixer, did:chio:z6MkrT2…8wd
issued by  your kernel 4f1c…a2e9
grants     none

computer.agents

computer.agents(role: string, options: { count: number; reads?: Mailbox }): Promise<Agent[]>

Starts several agents of one role, numbered from 1, optionally reading a shared mailbox.

typescript
const reviewers = await project.agents("review", { count: 2, reads: reviews });
for (const reviewer of reviewers) console.log(reviewer.summary);
Output
reviewer-1   did:chio:z6MkvR1…3mc   reads reviews
reviewer-2   did:chio:z6MkvR2…9tf   reads reviews

computer.fork

computer.fork(options?: { boot?: Program }): Promise<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.

typescript
const fork = await project.fork();
console.log(fork.describe());
Output
fork       51c07ae of quickstart, base main 7c2e1d0
writes     stay in the fork until you apply them

fork.grant

fork.grant(spec: { to: Agent | Partner | string; resources: Record<string, Right[]>; budget?: Money; expiresIn: string; under?: Treaty; delegation?: { depth: number; fanout: number } }): Authority

Issues a capability token over part of the fork: resources and their rights, a budget charged at your door, and an expiry. to takes an agent, a partner, or a DID. delegation lets the holder delegate narrower grants onward, and under issues a grant within a treaty. Issuance completes durably before the call returns.

typescript
const authority = fork.grant({
  to: fixer,
  resources: { "/workspace": ["read", "write"], "/models/default": ["invoke"] },
  budget: USD(2),
  expiresIn: "20m",
});
console.log(authority.describe());
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

authority.reissue(options: { on: Fork; resources?: Record<string, Right[]>; budget?: Money; expiresIn?: string }): Authority

Issues a grant on another fork with the same terms, or with the resources, budget, or lifetime you override. Your kernel signs the new grant; the original is unchanged.

typescript
const next = await project.fork();
const again = authority.reissue({ on: next });
console.log(again.describe());
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

authority.delegate

authority.delegate(spec: { to: Agent; resources: Record<string, Right[]>; budget?: Money; expiresIn?: string }): 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 AttenuationError, and nothing is issued.

typescript
// authority was granted with delegation: { depth: 1, fanout: 2 }
const tester = await project.agent("tester");
const child = authority.delegate({
  to: tester,
  resources: { "/workspace": ["read"] },
  budget: USD("0.50"),
  expiresIn: "10m",
});
console.log(child.describe());
Output
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

authority.revoke(): Promise<void>

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.

typescript
await authority.revoke();
Output
revoked    cap-01d2…2a90, by you
cascade    cap-01d2…6c13 (tester), refused from its next call

authority.token

authority.token: CapabilityToken

The signed capability token behind the grant, in its wire form. The Computer and the Kernel reads one field by field.

typescript
console.log(JSON.stringify(authority.token, null, 2));
Output
{
  "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

agent.run(task: string, options: { on: Fork; authority: Authority }): Promise<Run>

Sends a task to an agent under a grant. Every operation the agent attempts reaches your kernel before it reaches a resource.

typescript
const run = await fixer.run("fix issue #412", { on: fork, authority });
console.log(run.describe());
Output
run        run-01a8…e7c0
agent      fixer, on fork 51c07ae
authority  cap-01a8…31f2

run.events

run.events(options?: { include?: Agent[] }): AsyncIterable<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.

typescript
for await (const event of run.events()) console.log(event.summary);
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

partner.exec(task: string, options: { on: Fork; authority: Authority; reportTo?: Mailbox }): Promise<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.joinTree() waits for every agent the partner delegated to.

typescript
const run = await partner.exec("fix issue #414", { on: fork, authority, reportTo: reviews });
for await (const event of run.events()) console.log(event.summary);
await run.joinTree();
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

fork.diff(): Promise<string>

Returns the changes the fork holds against its base, as a unified diff.

typescript
console.log(await fork.diff());
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

computer.apply(changes: ChangeSet, options?: { treaty?: Treaty }): Promise<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.

typescript
const merged = await project.apply(fork.changes);
console.log(merged.describe());
Output
applied    fork 51c07ae onto quickstart main
revision   main 8d4b2e7, base 7c2e1d0
receipt    rcpt-01a8…d51e, signed by your kernel

computer.history.receipts

computer.history.receipts(filter?: { outcome?: "allow" | "deny" | "pending"; agent?: Agent | string; rule?: string; service?: string; since?: string }): Promise<Receipt[]>

Reads signed receipts from the computer's history, filtered by outcome, agent, rule, service, or time. Every filter combines with the others.

typescript
const [refusal] = await project.history.receipts({ outcome: "deny" });
console.log(refusal.describe());
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

Receipt

interface Receipt { id: string; request: string; summary: string; raw: string; describe(): string }

One signed decision. summary is one line for listings, describe is the full card, and raw is the canonical JSON your kernel signed.

typescript
console.log(refusal.raw);
Output
{"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"}

computer.history.export

computer.history.export(dir: string, options?: { agent?: Agent | string }): Promise<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.

typescript
const evidence = await project.history.export("audit/");
console.log(await evidence.verify());
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

evidence.verify

evidence.verify(options?: { ledger?: boolean }): Promise<Verification>

Checks every signature in an exported evidence package against your pinned kernel key, offline. ledger: true 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.

typescript
const evidence = await project.history.export("audit/");
console.log(await evidence.verify({ ledger: true }));
Output
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

computer.spend.stream

computer.spend.stream(filter?: { capability?: string; cursor?: string }): AsyncIterable<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.

typescript
for await (const event of project.spend.stream({ capability: "cap-01d2…6c13" })) {
  console.log(event.summary);
}
Output
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

computer.policy.add

computer.policy.add(rule: { name: string; allow?: Match; deny?: Match; egress?: { allow: string[] } }): Promise<Rule>

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.

typescript
const rule = await project.policy.add({
  name: "tests-are-evidence",
  deny: { write: "/workspace/tests/**" },
});
console.log(rule.describe());
Output
rule       tests-are-evidence
denies     write /workspace/tests/**
policy     sha256 9a41…e6f2, narrows every grant on quickstart

computer.policy.describe

computer.policy.describe(): Promise<string>

Describes the policy in force: its hash and the one it replaced, your rules, and the defaults every computer starts with.

typescript
console.log(await project.policy.describe());
Output
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

computer.policy.hash

computer.policy.hash(): Promise<string>

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.

typescript
console.log(await project.policy.hash());
Output
sha256:9e42…c5d1

computer.service

computer.service(name: string, options: { tools: Record<string, string> }): Promise<Service>

Exposes project functions as named tools and publishes them over MCP and HTTP. Every caller presents a grant from your kernel.

typescript
const pricing = await project.service("pricing", {
  tools: { quote: "src/pricing.py:quote", refund: "src/pricing.py:refund" },
});
const endpoint = await pricing.publish({ protocols: ["mcp", "http"] });
console.log(endpoint.describe());
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

service.grant(spec: { to: string; tools: string[]; limits?: { calls: number; per: string }; budget?: Money; expiresIn: string }): Authority

Grants a caller named tools on the service, with a call rate, a budget spent at each tool's price, and an expiry.

typescript
const access = pricing.grant({
  to: "did:chio:z6MkbY4…e1R",
  tools: ["quote"],
  limits: { calls: 100, per: "1h" },
  budget: USD(2),
  expiresIn: "24h",
});
console.log(access.describe());
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

service.require(rule: { approval: "you" | string; when: string }): Promise<Rule>

Pauses each matching call until the approver signs for that exact request.

typescript
const rule = await pricing.require({ approval: "you", when: "quote.percent > 50" });
console.log(rule.describe());
Output
rule       quote.percent > 50 waits for did:chio:z6Mkf3…Qp9 (you)
binds      one signature to one request

service.settle

service.settle(tool: string, terms: { rail: string; chain: string; token: string; payee: string }): Promise<SettlementTerms>

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.

typescript
const terms = await pricing.settle("refund", {
  rail: "eip3009",
  chain: "eip155:8453",
  token: "USDC",
  payee: "refund.wallet",
});
console.log(terms.describe());
Output
settles    refund over eip3009 on eip155:8453 in USDC, payee from refund.wallet
binding    chain, payee, amount, token contract, dispatch, capability, expiry

service.price

service.price(tool: string, terms: { amount: Money; economy?: string; split?: { to: string; bps: number; vest?: { cliff: string; end: string }; cosigners?: string[] }[] }): Promise<ToolPricing>

Sets a tool's price per call, the economy it settles in, and the payee split, one entry per beneficiary in split. 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.

typescript
const priced = await pricing.price("quote", {
  amount: USD("0.01"),
  economy: "urn:chio:economy:pricing",
  split: [
    { to: "did:chio:z6Mkf3…Qp9", bps: 5000 },
    {
      to: "did:chio:z6MkpA7…c2T",
      bps: 3333,
      vest: { cliff: "2026-11-01", end: "2027-01-30" },
      cosigners: ["did:chio:z6Mkf3…Qp9"],
    },
    { to: "urn:chio:economy:pricing:reserve", bps: 1667 },
  ],
});
console.log(priced.describe());
Output
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 bps

service.events

service.events(): AsyncIterable<CallEvent>

Streams calls to the service as your kernel decides them.

typescript
for await (const call of pricing.events()) console.log(call.summary);
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

service.usage(options: { since: string }): Promise<Usage>

Summarizes each caller's calls, refusals, approvals, and spend, computed from that caller's signed receipts.

typescript
console.log(await pricing.usage({ since: "1h" }));
Output
caller             calls   refused   approvals   spent
z6MkbY4…e1R        100     2         1           USD 1.00
z6MkQx8…7Ha        0       1         0           USD 0.00

computer.treaty

computer.treaty(terms: { partner: Partner; actions: string[]; expiresIn: string }): Promise<Treaty>

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.

typescript
const treaty = await project.treaty({
  partner,
  actions: ["workspace.read", "workspace.write", "model.invoke", "merge"],
  expiresIn: "7d",
});
console.log(treaty.describe());
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

treaty.revoke

treaty.revoke(): Promise<void>

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.

typescript
await treaty.revoke();
for await (const event of run2.events()) console.log(event.summary);
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

computer.mailbox(name: string, options: { partner: Partner; treaty: Treaty }): Promise<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.

typescript
const reviews = await project.mailbox("reviews", { partner, treaty });
console.log(reviews.describe());
Output
mailbox    reviews, under trt-01aa…4c19
senders    partner's agents, each under its own grant
readers    your agents that hold a reads grant

firstPassing

firstPassing(runs: Run[], options: { check: string }): Promise<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.

typescript
import { firstPassing } from "@chio-protocol/sdk";

const ours = await fixer.run("fix issue #414", { on: forkA, authority: grantA });
const theirs = await partner.exec("fix issue #414", { on: forkB, authority: grantB });
const winner = await firstPassing([ours, theirs], { check: "pytest" });
console.log(winner.describe());
await project.apply(winner.changes);
Output
winner     run-01ab…0c7d, partner on fork c4e81b7
check      pytest: 14 passed
others     cancelled, receipts kept in history

parallel and pipe

parallel(...programs: Program[]): Program; program.pipe(next: Program): Program

Composes boot programs, the pinned descriptors your project exports. parallel runs programs at once; pipe runs them in sequence. TypeScript has no operator overloading, so this is the canonical form the Python and Rust operators normalize to. A fork started with a boot program runs it under the fork's grant.

typescript
import { parallel } from "@chio-protocol/sdk";
import { reproduce, survey, patch, verify } from "./programs";

const boot = parallel(reproduce, survey).pipe(patch).pipe(verify);
const fork = await project.fork({ boot });
console.log(fork.describe());
Output
boot       (reproduce & survey) | patch | verify
fork       a3d90f1 of quickstart, base main 2f60a91

USD

USD(amount: number | string): Money

Constructs an exact dollar amount for budgets and prices. Pass a decimal string for cents, so the amount never passes through a floating-point number.

typescript
import { USD } from "@chio-protocol/sdk";

console.log(USD(2).toString());
console.log(USD("0.01").times(100).toString());
Output
USD 2.00
USD 1.00

ApplyConflict

class ApplyConflict extends ChioError { base: string; current: string }

Apply refuses to publish when main moved after the fork was taken. Nothing changes; fork again from the new main and reapply.

typescript
import { ApplyConflict } from "@chio-protocol/sdk";

try {
  await project.apply(fork.changes);
} catch (error) {
  if (!(error instanceof ApplyConflict)) throw error;
  console.log(error.message);
}
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.

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-protocol/process. Every method returns a Promise; describe is synchronous. Documents keep their wire field names, so program_id and command_id stay snake case. The client never retries a side effect and never generates a replacement command id.

bash
npm install @chio-protocol/process

The 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 readFileSync and writeFileSync come from node:fs.

WorkClient.open

WorkClient.open(state: string): Promise<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.

typescript
import { WorkClient } from "@chio-protocol/process";

const work = await WorkClient.open("./work");

work.select

work.select(offer: Signed<WorkOffer>, options: { programId: string; commandId: string }): Promise<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.

typescript
const offer = JSON.parse(readFileSync("partner-offer.json", "utf8"));
const draft = await work.select(offer, {
  programId: "pricing-openapi-auth",
  commandId: "auth-select-1",
});
writeFileSync("auth-draft.json", JSON.stringify(draft.commitment, null, 2));
console.log(draft.describe());
Output
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

work.prepare

work.prepare(proposal: WorkPreparationV1): Promise<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.

typescript
const prepared = await work.prepare({
  schema: "chio.work.preparation.v1",
  program_id: "pricing-openapi-auth",
  command_id: "auth-agree-1",
  proposal: {
    Agreement: {
      commitment: draft.commitment,
      funding_profile_id: "work-claim-escrow",
      terms_reference: "terms/openapi-auth.json",
    },
  },
});
console.log(prepared.describe());
Output
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:00Z

work.submit

work.submit(command: WorkCommandV1): Promise<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.

typescript
const commitment = JSON.parse(readFileSync("auth-commitment.json", "utf8"));
const submitted = await work.submit({
  schema: "chio.work.command.v1",
  program_id: "pricing-openapi-auth",
  command_id: "auth-submit-1",
  action: { Submit: { commitment } },
});
console.log(submitted.describe());
Output
command     auth-submit-1  Applied at revision 4
admitted    by kernel 9b07…d31c under its own policy

result.handle

result.handle: 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. Reading it on a Pending or Refused outcome throws WorkError; query the original command id instead.

typescript
console.log(JSON.stringify(submitted.handle, null, 2));
Output
{
  "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

work.inspect(handle: WorkHandleV1): Promise<WorkViewV1>

Reads the work view for a handle. It is a query, so it mutates nothing and dispatches no checker.

typescript
console.log((await work.inspect(submitted.handle)).describe());
Output
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

work.query(query: WorkQueryV1): Promise<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.

typescript
const found = await work.query({ Command: { command_ref: "auth-submit-1" } });
console.log(found.describe());
Output
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

work.collect(handle: WorkHandleV1): Promise<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.

typescript
const view = await work.collect(submitted.handle);
console.log(view.describe());
Output
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

interface WorkCommitmentV1 { handle: WorkHandleV1; permit: Signed<DispatchPermit>; profile_ref: WorkProfileRefV1; custody_ref: string; graph_ref: string; funding?: 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.

typescript
const commitment = JSON.parse(readFileSync("auth-commitment.json", "utf8"));
console.log(JSON.stringify(commitment.funding, null, 2));
Output
{
  "rail": "work-claim-escrow",
  "agreement_digest": "7e1d…c04a",
  "reserve_ref": "0x8c1f…40d2"
}

WorkViewV1

interface WorkViewV1 { handle: WorkHandleV1; execution: Observation; acceptance: Observation; results: Observation; recovery: Observation; settlement: Observation; delivery: Observation; describe(): 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.

typescript
console.log(JSON.stringify(view.acceptance, null, 2));
Output
{
  "Available": {
    "source": "verify.corbel.example",
    "revision": 1,
    "observed_at": "2026-10-15T18:04Z",
    "value": { "decision": "Accepted", "predicate": "match", "checker": "c7a1…02be" }
  }
}

WorkError

class WorkError extends Error { code: string; commandRef?: string }

A stable code and the original command reference. After a timeout, query that reference; the outcome the owner recorded is the answer.

typescript
import { WorkError } from "@chio-protocol/process";

try {
  await work.submit(command);
} catch (error) {
  if (!(error instanceof WorkError)) throw error;
  const found = await work.query({ Command: { command_ref: "auth-submit-1" } });
  console.log(found.describe());
}
Output
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 session client

The package also exports the protocol-level clients the Computer is built on: ChioClient and ChioSession for hosted MCP sessions, the invariants for offline verification, ReceiptQueryClient, and the cognition-market clients. Use them to call a kernel edge you reach over the network, such as the pricing service another operator publishes, or to verify evidence without a computer.

Entry points

The manifest declares 3 importable subpaths. The root re-exports every module, so an import from the root reaches the same symbols; the two narrow subpaths exist so a bundler can drop the rest.

Import specifierWhat it carries
@chio-protocol/sdk
The root barrel: ChioClient, ChioSession, staticBearerAuth, the JSON-RPC types, the transport functions, every invariant, the error classes, signDpopProof, ReceiptQueryClient, findingBidCeiling, and the cognition-market clients.
@chio-protocol/sdk/invariants
Verification only: canonical JSON, SHA-256, Ed25519, and receipt, capability, and manifest parsing and verification.
@chio-protocol/sdk/transport
Streamable HTTP: RPC message parsing, session lifecycle, header builders.

Invariants

The invariants module has no dependency outside node:crypto, and every exported function is synchronous, so there is nothing to await and no network call to make. This is the full export list of src/invariants/index.ts.

ModuleExports
json.ts
canonicalizeJson, canonicalizeJsonString, and the types JsonPrimitive and JsonValue.
crypto.ts
sha256Hex, verifyChioSignature.
hashing.ts
sha256HexBytes, sha256HexUtf8.
receipt.ts
parseReceiptJson, receiptBodyCanonicalJson, receiptSigningBodyCanonicalJson, verifyReceipt, verifyReceiptJson, verifyReceiptWithTrustedSigners, and the types ChioReceipt, ReceiptDecisionKind, ReceiptVerification.
capability.ts
capabilityBodyCanonicalJson, capabilitySigningBody, capabilitySigningBodyCanonicalJson, parseCapabilityJson, verifyCapability, verifyCapabilityJson, and the types CapabilityTimeStatus, CapabilityToken, CapabilityVerification, DelegationLink.
manifest.ts
parseSignedManifestJson, signedManifestBodyCanonicalJson, verifySignedManifest, verifySignedManifestJson, and the types ManifestVerification, MonetaryAmount, PricingModel, SignedManifest, ToolManifest, ToolPricing.
signing.ts
isValidPublicKeyHex, isValidSignatureHex, publicKeyHexMatches, signJsonStringEd25519, signUtf8MessageEd25519, verifyJsonStringSignatureEd25519, verifyUtf8MessageEd25519, and the types CanonicalJsonSignature, Utf8MessageSignature.
errors.ts
ChioInvariantError, parseJsonText, and the type ChioInvariantErrorCode.

DPoP proof signing is not here. It lives on the root entry point as signDpopProof. See DPoP proofs.

Canonical JSON

typescript
import { canonicalizeJson, canonicalizeJsonString } from "@chio-protocol/sdk/invariants";

// Serialize a value to its RFC 8785 canonical JSON string.
const canonical: string = canonicalizeJson({ b: 2, a: 1 });
// canonical === '{"a":1,"b":2}'

// Canonicalize an existing JSON string.
const str: string = canonicalizeJsonString('{"b":2,"a":1}');
// str === '{"a":1,"b":2}'

Hashing

typescript
import { sha256Hex, sha256HexBytes, sha256HexUtf8 } from "@chio-protocol/sdk/invariants";

sha256Hex("hello world");            // string or Buffer
sha256HexBytes(Buffer.from([1, 2, 3]));
sha256HexUtf8("hello world");

Receipts

verifyReceipt is synchronous and takes an optional list of trusted signer keys. It returns a ReceiptVerification whose field names follow the shared Rust binding contract.

typescript
import {
  parseReceiptJson,
  receiptBodyCanonicalJson,
  verifyReceipt,
  verifyReceiptJson,
  verifyReceiptWithTrustedSigners,
} from "@chio-protocol/sdk/invariants";
import type { ChioReceipt, ReceiptVerification } from "@chio-protocol/sdk/invariants";

const receipt: ChioReceipt = parseReceiptJson(jsonString);

// signature_valid, parameter_hash_valid, receipt_id_valid, decision,
// receipt_kind, boundary_class, trust_level, result, authorized,
// signer_key_hex, signer_trusted, ok
const result: ReceiptVerification = verifyReceipt(receipt);

// Same call with the signer allow-list spelled out.
const trusted = verifyReceiptWithTrustedSigners(receipt, [kernelKeyHex]);

const quick: ReceiptVerification = verifyReceiptJson(jsonString);
const body: string = receiptBodyCanonicalJson(receipt);

ok and authorized both require signer_trusted, so a call that passes no trusted signer returns false for both however good the signature is. Branch on signature_valid and parameter_hash_valid and receipt_id_valid for an integrity check, and pass the kernel keys you accept when you want an authorization answer.

Capabilities

typescript
import {
  capabilitySigningBodyCanonicalJson,
  parseCapabilityJson,
  verifyCapability,
  verifyCapabilityJson,
} from "@chio-protocol/sdk/invariants";
import type {
  CapabilityToken,
  CapabilityVerification,
  CapabilityTimeStatus,
} from "@chio-protocol/sdk/invariants";

const cap: CapabilityToken = parseCapabilityJson(jsonString);
const now = Math.floor(Date.now() / 1000);

// The third argument bounds the delegation chain; omit it for no bound.
const status: CapabilityVerification = verifyCapability(cap, now, 4);
// status.signature_valid: boolean
// status.delegation_chain_shape_valid: boolean
// status.time_valid: boolean
// status.time_status: "valid" | "not_yet_valid" | "expired"

Manifests

typescript
import {
  parseSignedManifestJson,
  verifySignedManifest,
  verifySignedManifestJson,
} from "@chio-protocol/sdk/invariants";
import type { SignedManifest, ManifestVerification } from "@chio-protocol/sdk/invariants";

const manifest: SignedManifest = parseSignedManifestJson(jsonString);

// structure_valid, signature_valid, embedded_public_key_valid,
// embedded_public_key_matches_signer
const result: ManifestVerification = verifySignedManifest(manifest);

Ed25519

typescript
import {
  signJsonStringEd25519,
  signUtf8MessageEd25519,
  verifyJsonStringSignatureEd25519,
  verifyUtf8MessageEd25519,
  isValidPublicKeyHex,
  isValidSignatureHex,
  publicKeyHexMatches,
} from "@chio-protocol/sdk/invariants";

const sig = signJsonStringEd25519('{"key":"value"}', privateKeyHex);
const ok = verifyJsonStringSignatureEd25519(
  '{"key":"value"}',
  sig.public_key_hex,
  sig.signature_hex,
);

isValidPublicKeyHex(hex);  // 64 hex chars
isValidSignatureHex(hex);  // 128 hex chars

ChioClient and ChioSession

ChioClient composes transport, authentication, and session management. initialize() runs the MCP handshake against a Chio edge and returns a ChioSession. The constructor takes baseUrl, the fields of StaticBearerAuth, and an optional fetchImpl. The base URL is the edge origin: the transport appends /mcp to it on every request. initialize() sends protocol version 2025-11-25 and client info naming the package unless you pass protocolVersion, capabilities, or clientInfo.

Initialize an authenticated session

This is an outside buyer's agent opening a session on the pricing service from Quickstart part 2. The base URL is the service origin without /mcp; CHIO_AUTH_TOKEN holds the token for the grant you issued to the buyer.

typescript
import { ChioClient } from "@chio-protocol/sdk";

const token = process.env.CHIO_AUTH_TOKEN;
if (!token) throw new Error("Set CHIO_AUTH_TOKEN to the token for your grant");

const client = ChioClient.withStaticBearer("https://127.0.0.1:9440/pricing", token);
const session = await client.initialize();
try {
  console.log(await session.listTools());
} finally {
  await session.close();
}

A successful initialization returns a session with sessionId and negotiated protocolVersion. The listing names only the tools the grant covers: quote, not refund.

Session method index

ChioSession numbers its own JSON-RPC request ids from 2, the handshake having taken 1. Every method below returns the RPC result, except request, sendEnvelope, notification, and setLogLevel, which return the whole RpcExchange, and close, which returns the status and headers of the session delete.

MethodJSON-RPC method
listTools(params?)
tools/list
callTool(name, args?)
tools/call
listResources(params?)
resources/list
readResource(uri)
resources/read
subscribeResource(uri)
resources/subscribe
unsubscribeResource(uri)
resources/unsubscribe
listResourceTemplates(params?)
resources/templates/list
listPrompts(params?)
prompts/list
getPrompt(name, args?)
prompts/get
complete(params)
completion/complete
setLogLevel(level)
logging/setLevel, sent as a notification
listTasks(params?)
tasks/list
getTask(taskId)
tasks/get
getTaskResult(taskId)
tasks/result
cancelTask(taskId)
tasks/cancel
request(method, params?, onMessage?)
any method, returning the exchange
requestResult(method, params?, onMessage?)
any method, returning the terminal response
notification(method, params?, onMessage?)
any notification
sendEnvelope(body, onMessage?)
a caller-built JSON-RPC envelope
setMessageHandler(onMessage)
replaces the default per-message handler
close()
deletes the session over HTTP

A JSON-RPC failure raises an Error carrying the server's message on every result-returning method. request returns the full exchange, including failure envelopes. requestResult uses the terminal-message helper and also throws on an RPC error; use request to retain the error object.

Call a tool and inspect its outcome

callTool(name, args?): Promise<unknown> unwraps the JSON-RPC result and throws a plain Error for an RPC failure. Use request when you need the error code and data; requestResult also passes through the terminal-message helper, which throws on RPC errors. The fragment below asks the initialized session for a quote.

typescript
import { isJsonRpcFailure } from "@chio-protocol/sdk";

type ToolResult = { isError?: boolean; content?: unknown[] };

const exchange = await session.request("tools/call", {
  name: "quote",
  arguments: { sku: "sku-118", percent: 10 },
});
const response = exchange.messages.find(
  (message) => message.id === exchange.request.id && !message.method,
);
if (!response) throw new Error("No terminal RPC response");
if (isJsonRpcFailure(response)) {
  console.error("RPC rejected:", response.error.code, response.error.message);
  console.error(response.error.data);
} else {
  const result = response.result as ToolResult;
  if (result.isError) console.error("Tool reported failure:", result.content);
  else console.log("Tool result:", result);
}

The buyer's grant allows quote, so this call returns a price. The same request for refund returns an RPC failure whose data carries the refusal and its receipt id. A transport error or an absent receipt is not an authorization decision.

Transport

The transport module implements MCP Streamable HTTP with an explicit session lifecycle. Use it directly to manage sessions, or let ChioClient manage them. Every function appends /mcp to the base URL it is given, so pass the edge origin rather than the endpoint path. The caller supplies whole JSON-RPC envelopes; initializeSession posts the one you hand it, reads MCP-Session-Id off the response, and sends notifications/initialized for you.

typescript
import {
  initializeSession,
  postRpc,
  postNotification,
  deleteSession,
  buildRpcHeaders,
  buildSessionDeleteHeaders,
  parseRpcMessages,
  readRpcMessagesUntilTerminal,
  terminalMessage,
} from "@chio-protocol/sdk/transport";
import type {
  SessionState,
  InitializeSessionResult,
  RpcExchange,
  JsonRpcMessage,
} from "@chio-protocol/sdk/transport";

// InitializeSessionResult extends SessionState, so it carries sessionId and
// protocolVersion alongside both handshake exchanges.
const session: InitializeSessionResult = await initializeSession(
  "https://edge.example.com",
  authToken,
  {
    jsonrpc: "2.0",
    id: 1,
    method: "initialize",
    params: {
      protocolVersion: "2025-11-25",
      capabilities: {},
      clientInfo: { name: "basalt-buyer", version: "1.0.0" },
    },
  },
);

const exchange: RpcExchange = await postRpc(
  "https://edge.example.com",
  authToken,
  session.sessionId,
  session.protocolVersion,
  { jsonrpc: "2.0", id: 2, method: "tools/list", params: {} },
);
// exchange.request, exchange.status, exchange.headers, exchange.messages

await deleteSession("https://edge.example.com", authToken, session.sessionId);

DPoP proofs

signDpopProof builds a canonical proof body, hashes the tool arguments with SHA-256, and signs the body with the agent Ed25519 seed. The schema id is chio.dpop_proof.v1, exported as DPOP_SCHEMA, and the kernel checks the same value in verify_dpop_proof. A bad seed raises DpopSignError.

typescript
import { signDpopProof, DPOP_SCHEMA } from "@chio-protocol/sdk";
import type { DpopProof, DpopProofBody } from "@chio-protocol/sdk";

const proof: DpopProof = signDpopProof({
  capabilityId: process.env.CHIO_CAPABILITY_ID!,
  toolServer: "workspace",
  toolName: "read_file",
  actionArgs: { path: "/workspace/src/pricing.py" },
  agentSeedHex: process.env.CHIO_AGENT_SEED_HEX!,
  nonce: "optional-server-nonce",
  issuedAt: 1744537862, // optional, defaults to now
});

// proof.body fields, in the alphabetical order canonical JSON puts them:
//   action_hash, agent_key, capability_id, issued_at, nonce,
//   schema (= DPOP_SCHEMA), tool_name, tool_server
// proof.signature is a hex-encoded Ed25519 signature over the canonical body.

ReceiptQueryClient

ReceiptQueryClient wraps GET /v1/receipts/query and injects the bearer token. The constructor is positional: (baseUrl, authToken, fetchImpl?). Filter fields are camelCase in TypeScript and go on the query string under the same names.

typescript
import { ReceiptQueryClient } from "@chio-protocol/sdk";
import type { ChioReceipt, ReceiptQueryResponse } from "@chio-protocol/sdk";

const client = new ReceiptQueryClient(
  "https://receipts.example.com",
  process.env.RECEIPT_TOKEN!,
);

const page: ReceiptQueryResponse = await client.query({
  capabilityId: "cap-01a8…31f2",
  agentSubject: agentPublicKeyHex,
  toolServer: "workspace",
  toolName: "read_file",
  outcome: "deny",
  since: 1744500000,        // Unix seconds
  until: 1744600000,
  minCost: 1n,              // bigint, not number
  maxCost: 1000n,
  costCurrency: "USD",      // three uppercase letters; required with a cost bound
  limit: 50,
  cursor: undefined,        // numeric cursor from a prior page
});

console.log(page.totalCount, page.nextCursor, page.receipts.length);

paginate() is an async generator over pages. It drives cursor from each response's nextCursor, stops when the server omits it, and throws QueryError when a cursor fails to advance rather than looping.

typescript
for await (const receipts of client.paginate({ outcome: "deny" })) {
  for (const receipt of receipts) {
    const r: ChioReceipt = receipt;
    console.log(r.id, r.tool_name, r.decision?.verdict);
  }
}

A non-2xx response throws QueryError with the HTTP status on .status; a failed fetch throws TransportError. The wire contract, including the values outcome accepts and the server's pagination limits, is on the Receipt Query API page.

Cognition market

The root entry point also carries the Finding market clients. A buyer searches, verifies a proof bundle, and purchases; a seller packages a verified fix and admits or retracts it; a hosted client speaks the multi-tenant API. Verification is not reimplemented in TypeScript: the buyer shells out to the chio binary and parses its JSON, so the Rust verifier stays the only judge of a bundle.

CognitionMarketBuyer

Constructed from a buyer profile path and an options object (chioBinary, fetch, statusFloorPath, timeoutMs, default 30000). The profile must carry the schema chio.finding.buyer-client.v1. The status floor defaults to the profile path with .status-floor.json appended.

MethodBehavior
search(options)
GET /v1/findings/search with topicPrefix, limit (default 20), and an optional cursor. An empty or untrimmed prefix throws before the request.
proof(findingId)
GET /v1/findings/{id}/proof, returning the bundle bytes. The id must be lowercase 64-hex, and the response is bounded at 24 MiB.
verifyProof(proof)
Pipes the bundle into chio finding verify-bundle --profile <path> --input - --json and returns the Finding id, the bytes, and the verifier report.
verifiedProof(findingId)
Fetch then verify, refusing a bundle that names a different Finding.
purchase(verified, options)
POST /v1/findings/{id}/purchase with a canonical request built from maxPriceUnits, currency (default USD), and deadlineSecs (default 3600). The terminal is verified before the call returns.
purchaseVerifiedFix(verified, options)
The same purchase, returning a PurchasedVerifiedFix: repository, base and candidate revisions, and the patch text. Nothing is written to a workspace.
status(findingId)
Runs chio finding status against the profile's feed, operator authorization, service bond, rollback floor, and epoch age bound, then maps non_inclusion to live and inclusion to retracted.
challenge(findingId, signedChallenge)
POST /v1/findings/{id}/challenges with caller-signed bytes, bounded at 1 MiB.
challengeEvidenceInvalid(verified, purchased, options?)
Re-verifies the purchase terminal, builds the evidence document, and files it with chio finding challenge --class evidence-invalid. Requires a signing seed on the profile.
typescript
import { CognitionMarketBuyer, CognitionMarketError } from "@chio-protocol/sdk";
import type { PurchasedVerifiedFix, VerifiedFindingProof } from "@chio-protocol/sdk";

const buyer = new CognitionMarketBuyer("./buyer-profile.json");

const page = await buyer.search({ topicPrefix: "repo/parser", limit: 20 });

const verified: VerifiedFindingProof = await buyer.verifiedProof(findingId);
const live = await buyer.status(findingId);
if (live.status !== "live") throw new CognitionMarketError("finding is retracted");

const fix: PurchasedVerifiedFix = await buyer.purchaseVerifiedFix(verified, {
  maxPriceUnits: 300,
  currency: "USD",
});
console.log(fix.repository, fix.baseRevision, fix.candidateRevision);

CognitionMarketSeller

Constructed from a credential path carrying the schema chio.finding.seller-client.v1. The default request timeout is 720000 milliseconds, sized for the operator's bounded packaging sandbox.

  • packageVerifiedFix(input) builds the submission identity (repository, base and candidate revisions, tests, topic, price) and derives its requestId as SHA-256 over the domain-separated canonical JSON. Price defaults to 300 and must fall between 1 and the verified-fix sale exposure of 450 units. The repository must be an absolute normalized operator-side path, and a caller-supplied output is refused because the package is operator-owned.
  • admit(packageRequest) posts the package to POST /v1/findings/operator/verified-fixes.
  • retract(findingId) posts chio.finding.voluntary-retraction-request.v1 to POST /v1/findings/operator/retractions, with a request id derived from the retraction domain and the Finding id.

HostedCognitionMarketClient

The multi-tenant transport. The constructor takes endpoint, tenantId, apiKeyId, apiKeySecret, and optional fetch and timeoutMs. The endpoint must be an https: URL with no embedded credentials. Every request carries chio-api-key-id, chio-api-key-secret, chio-request-id, and chio-tenant-id; a request with a body adds idempotency-key.

  • publish(finding, requestId, idempotencyKey): POST /v1/findings/publish.
  • mutate(operation, mutation, requestId): POST /v1/findings/events/{operation}, where the operation is one of listing, delivery, challenge, verified-fix, retraction, and penalty. The mutation carries aggregateId, eventId, a non-negative expectedRevision, and the payload; the event id doubles as the idempotency key.
  • finding(findingId, requestId): GET /v1/findings/{id}.
  • findings(options): GET /v1/findings with an optional after and a limit from 1 through 100.

Every failure on all three clients raises CognitionMarketError: a non-2xx response, an oversized body, a timeout, a profile whose schema does not match, and a verifier answer that names a different Finding. The class does not extend ChioError.

findingBidCeiling

findingBidCeiling computes a buyer-local ceiling with exact integer arithmetic and returns it as a decimal string. It authenticates nothing. What it does is bind one caller-carried estimate to the buyer's own expected source, context, and replay-recipe digests, to the buyer's currency, and to a validity window, then discount the estimate by three basis-point factors and cap the result at the remaining budget. Arithmetic runs in BigInt and rounds down once, after the combined product.

typescript
import { findingBidCeiling, FindingBidCeilingError } from "@chio-protocol/sdk";
import type {
  BuyerFindingEstimate,
  FindingBidCeilingInput,
  FindingBidCeilingPolicy,
} from "@chio-protocol/sdk";

const estimate: BuyerFindingEstimate = {
  units: "1200",
  currency: "USD",
  provenance: "buyer_metering_history_v1",
  sourceSha256: expectedSourceSha256,
  contextSha256: expectedContextSha256,
  replayRecipeSha256: expectedReplayRecipeSha256,
  observedAtUnixMs: "1744500000000",
  validUntilUnixMs: "1744503600000",
};

const policy: FindingBidCeilingPolicy = {
  budgetRemainingUnits: "5000",
  currency: "USD",
  wouldHaveRunBps: "9000",       // 0 to 10000
  siblingRedundancyBps: "1500",  // subtracted from 10000
  guaranteeClassBps: "10000",
};

const input: FindingBidCeilingInput = {
  estimate,
  policy,
  expectedSourceSha256,
  expectedContextSha256,
  expectedReplayRecipeSha256,
  nowUnixMs: "1744501000000",
};

try {
  const ceiling: string = findingBidCeiling(input);
} catch (err) {
  if (err instanceof FindingBidCeilingError) console.error(err.code);
}

FindingBidCeilingError.code is one of invalid_decimal, u64_overflow, basis_points_out_of_range, currency_mismatch, provenance_unsupported, source_substituted, context_substituted, replay_recipe_substituted, digest_malformed, invalid_validity_window, and stale_estimate. Only two provenance values are accepted: buyer_metering_history_v1 and buyer_fresh_metered_quote_v1.

Framework and platform packages

Framework integrations, runtime bindings, the WASM guard guest SDK, and the scaffolder ship as separate, independently versioned npm packages rather than subpaths of @chio-protocol/sdk. Every package under sdks/ whose manifest is not marked private is below, with the version and the declared dependencies that manifest carries.

PackageVersionSubpathsDeclared dependencies
@chio-protocol/ai-sdk
0.1.0
.
none
@chio-protocol/ai-sdk-middleware
0.1.0
.
none
@chio-protocol/browser
0.1.0
., ./pkg/*
@chio-protocol/wasm-core
@chio-protocol/deno
0.1.0
., ./web/*
@chio-protocol/wasm-core
@chio-protocol/edge
0.1.0
., ./web/*
@chio-protocol/wasm-core
@chio-protocol/elysia
0.1.0
.
@chio-protocol/node-http
@chio-protocol/express
0.1.0
.
@chio-protocol/node-http
@chio-protocol/fastify
0.1.0
.
@chio-protocol/node-http, fastify-plugin
@chio-protocol/guard-ts
0.2.0
none
none
@chio-protocol/mobile
0.1.0
., ./expo-plugin
none
@chio-protocol/next
0.1.0
.
none
@chio-protocol/node-http
0.1.0
.
none
@chio-protocol/passkey
0.1.0
.
none
@chio-protocol/sdk
0.1.0
., ./invariants, ./transport
none
@chio-protocol/wasm-core
0.1.0
.
none
@chio-protocol/workers
0.1.0
., ./bundler/*
none
create-chio-app
0.1.0
.
none

The dependency column is the whole basis for two claims often made about these packages. @chio-protocol/elysia, @chio-protocol/express, @chio-protocol/fastify are the sidecar HTTP adapters: each declares @chio-protocol/node-http, the shared interception layer that evaluates a request against a chio sidecar, attaches the signed receipt, and surfaces the deny reason to the handler. No other listed package declares it, and none of them declares the in-process core @chio-protocol/sdk. @chio-protocol/browser, @chio-protocol/deno, @chio-protocol/edge declare @chio-protocol/wasm-core, the shared receipt-hex helpers the WASM runtimes build on.

A worked application

Your pricing service, written as an Express application, serves three routes: GET /healthz skipped, POST /v1/quote allowed for a caller whose grant covers quotes, and POST /v1/refund refused unless the grant names refunds. The middleware points at a sidecar URL, and the handler reads the receipt id off the request.

pricing/server.mjsjavascript
import express from "express";
import { chio } from "@chio-protocol/express";
import { quote } from "./pricing.mjs";

const app = express();
app.use(chio({ sidecarUrl: "http://127.0.0.1:9090", skip: ["/healthz"] }));
app.use(express.json());

app.get("/healthz", (_req, res) => {
  res.json({ status: "ok" });
});

app.post("/v1/quote", (req, res) => {
  res.json({
    price: quote(req.body.sku, req.body.percent),
    receipt_id: req.chioResult?.receipt.id ?? null,
  });
});

Fastify and Elysia take the same options through their own registration idiom:

pricing/server.mjs · Fastifyjavascript
import { chio } from "@chio-protocol/fastify";

await fastify.register(chio, {
  sidecarUrl: "http://127.0.0.1:9090",
  skip: ["/healthz"],
});

On an allow, the interception layer sets X-Chio-Receipt-Id to the receipt id and puts the evaluation result on req.chioResult. On a deny it answers with the verdict's HTTP status and a JSON body of error, message, receipt_id, and suggestion, where error is one of six CHIO_ERROR_CODES values: chio_access_denied, chio_sidecar_unreachable, chio_sidecar_unavailable, chio_evaluation_failed, chio_invalid_receipt, and chio_timeout. A sidecar that answers with a receipt that fails verification is a 502 with chio_invalid_receipt, not a pass.

Error hierarchy

ChioError is the base for transport, query, and DPoP-signing exceptions. It carries a string code. Catch the base class for broad recovery, or a subclass to branch on failure mode. DpopSignError has code dpop_sign_error, QueryError has query_error plus an optional HTTP status, and TransportError has transport_error.

typescript
import {
  ChioError,
  DpopSignError,
  QueryError,
  TransportError,
} from "@chio-protocol/sdk";

try {
  const page = await client.query({ capabilityId: "cap_abc" });
} catch (err) {
  if (err instanceof QueryError) {
    console.error("query failed", err.status, err.message);
  } else if (err instanceof TransportError) {
    console.error("network failed", err.message);
  } else if (err instanceof ChioError) {
    console.error("chio error", err.code, err.message);
  } else {
    throw err;
  }
}

Match the error family to the API

ChioInvariantError lives under @chio-protocol/sdk/invariants and does not extend ChioError. Its code is one of json, canonical_json, invalid_hex, invalid_public_key, and invalid_signature. CognitionMarketError and FindingBidCeilingError extend neither. Handle each family used by your application, plus the plain errors raised by session helpers.

Verify the call’s receipt

After the session example and tool call, obtain the matching receipt through your deployment’s receipt-query API. Save it as receipt.json. The verifier takes the kernel key from your trust file, which chio trust pin receipt-kernel ./larkfield.card writes, never from the receipt.

typescript
import { readFileSync } from "node:fs";
import { parseReceiptJson, verifyReceiptWithTrustedSigners } from "@chio-protocol/sdk/invariants";
import { TrustFile } from "@chio-protocol/sdk/trust";

const trust = await TrustFile.load(); // CHIO_TRUST, .chio/trust.toml, then ~/.chio/trust.toml
const receipt = parseReceiptJson(readFileSync("receipt.json", "utf8"));
const verification = verifyReceiptWithTrustedSigners(receipt, trust.keys("receipt-kernel"));

if (!verification.ok) throw new Error("Receipt integrity or signer trust failed");
console.log("Recorded decision:", verification.decision);
console.log("Authorizing receipt:", verification.authorized);

A valid, trusted denial receipt can have ok = true and authorized = false. Check the request, tool, policy context, and recorded decision as well as integrity. Never derive the trusted-key list from the receipt being checked.

DPoP signing is a separate primitive. callTool accepts a name and arguments; it does not automatically attach a separately signed proof. Use the proof-bearing transport contract required by your deployment.

Source

This reference follows the public TypeScript implementation: client, session, transport, invariants, authentication, DPoP, receipt-query, finding, and cognition-market modules. Package tables render the checked SDK manifests.

Read the Protocol Reference for the wire contract and Bindings API for cross-language verification semantics.

Conformance

The TypeScript SDK runs against the cross-language conformance vectors. Canonical JSON output, SHA-256 digests, Ed25519 signatures, receipt verification, capability verification, and manifest verification all produce byte-identical results against the Rust reference. The installed CLI runs the same vectors against the published package.

bash
chio conformance run --peer js