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.
| Task | API / package | Execution 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. |
0 entries. Choose a section or search the full text.
Install
npm install @chio-protocol/sdkThe 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.
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());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.
import { Computer } from "@chio-protocol/sdk";
const project = await Computer.open("quickstart");
console.log(project.describe());computer quickstart
owner did:chio:z6Mkf3…Qp9 (you)
revision main @ 7c2e1d0
resources /workspace git, 14 files
/models/default model route, metered in USD
/secrets 3 filesComputer.connect
Computer.connect(name: string): Promise<Partner>
Resolves a paired partner by the name you gave its card, and authenticates its kernel before returning.
const partner = await Computer.connect("partner");
console.log(partner.describe());partner partner, did:chio:z6MkpA7…c2T operator Halden, halden.example kernel 9b07…d31c, pinned from its card transport iroh, authenticated at handshake
Computer.partners
Computer.partners(): Promise<Partner[]>
Lists every partner you have paired with, each with its pinned kernel key and the treaty in force.
for (const partner of await Computer.partners()) console.log(partner.summary);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.
const fixer = await project.agent("fixer");
console.log(fixer.describe());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.
const reviewers = await project.agents("review", { count: 2, reads: reviews });
for (const reviewer of reviewers) console.log(reviewer.summary);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.
const fork = await project.fork();
console.log(fork.describe());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.
const authority = fork.grant({
to: fixer,
resources: { "/workspace": ["read", "write"], "/models/default": ["invoke"] },
budget: USD(2),
expiresIn: "20m",
});
console.log(authority.describe());grant cap-01a8…31f2, signed by your kernel
to fixer, did:chio:z6MkrT2…8wd
on quickstart, fork 51c07ae
allows /workspace read, write
/models/default invoke
budget USD 2.00, charged at your door
expires in 20 minutesauthority.reissue
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.
const next = await project.fork();
const again = authority.reissue({ on: next });
console.log(again.describe());grant cap-01a8…5b3e, signed by your kernel
to fixer, did:chio:z6MkrT2…8wd
on quickstart, fork 7d2e914
allows /workspace read, write
/models/default invoke
budget USD 2.00, charged at your door
expires in 20 minutesauthority.delegate
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.
// 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());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.
await authority.revoke();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.
console.log(JSON.stringify(authority.token, null, 2));{
"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.
const run = await fixer.run("fix issue #412", { on: fork, authority });
console.log(run.describe());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.
for await (const event of run.events()) console.log(event.summary);00:00 admitted fixer runs "fix issue #412" on fork 51c07ae 00:03 read /workspace/src/pricing.py 00:05 read /workspace/tests/test_pricing.py 00:09 refused read /secrets/.env: outside the grant 00:12 invoke /models/default, USD 0.21 00:41 write /workspace/src/pricing.py 00:56 test pytest -k issue_412: passed spent USD 0.48 of 2.00
partner.exec
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.
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();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.
console.log(await fork.diff());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.
const merged = await project.apply(fork.changes);
console.log(merged.describe());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.
const [refusal] = await project.history.receipts({ outcome: "deny" });
console.log(refusal.describe());receipt rcpt-01a8…9c04 request fixer read /secrets/.env verdict deny: outside the grant grant cap-01a8…31f2 policy sha256 3c7b…8010 signed your kernel 4f1c…a2e9, signature verifies
Receipt
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.
console.log(refusal.raw);{"action":{"parameter_hash":"5e0d…71c2","parameters":{"path":"/secrets/.env"}},"capability_id":"cap-01a8…31f2","decision":{"reason":"not in capability scope","verdict":"deny"},"evidence":[],"id":"rcpt-01a8…9c04","kernel_key":"4f1c…a2e9","policy_hash":"3c7b…8010","receipt_kind":"mediated_decision","signature":"9d2e…07b4","tool_name":"read","tool_server":"quickstart"}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.
const evidence = await project.history.export("audit/");
console.log(await evidence.verify());receipts 9 signed decisions from your kernel refusals 1: fixer read /secrets/.env spend USD 0.48 of 2.00 verified offline, against your kernel key 4f1c…a2e9
evidence.verify
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.
const evidence = await project.history.export("audit/");
console.log(await evidence.verify({ ledger: true }));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.
for await (const event of project.spend.stream({ capability: "cap-01d2…6c13" })) {
console.log(event.summary);
}seq 4308 15:46 triage-418 models USD 0.02 remaining USD 10.04 of USD 20.00 seq 4309 15:46 triage-419 models USD 0.02 remaining USD 10.02 of USD 20.00 seq 4310 15:47 triage-417 models USD 0.02 remaining USD 10.00 of USD 20.00 cursor 9c1d…77ae, complete through seq 4310, checkpoint 43
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.
const rule = await project.policy.add({
name: "tests-are-evidence",
deny: { write: "/workspace/tests/**" },
});
console.log(rule.describe());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.
console.log(await project.policy.describe());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.
console.log(await project.policy.hash());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.
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());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.
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());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.
const rule = await pricing.require({ approval: "you", when: "quote.percent > 50" });
console.log(rule.describe());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.
const terms = await pricing.settle("refund", {
rail: "eip3009",
chain: "eip155:8453",
token: "USDC",
payee: "refund.wallet",
});
console.log(terms.describe());settles refund over eip3009 on eip155:8453 in USDC, payee from refund.wallet binding chain, payee, amount, token contract, dispatch, capability, expiry
service.price
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.
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());price pricing.quote USD 0.01 per call, economy urn:chio:economy:pricing
split 5000 did:chio:z6Mkf3…Qp9
3333 did:chio:z6MkpA7…c2T, vests 2026-11-01 to 2027-01-30, cosigned by you
1667 urn:chio:economy:pricing:reserve
manifest re-signed, payee_split sums to 10000 bpsservice.events
service.events(): AsyncIterable<CallEvent>
Streams calls to the service as your kernel decides them.
for await (const call of pricing.events()) console.log(call.summary);00:00 z6MkbY4 quote sku-118 at 10% allow USD 0.01 00:04 z6MkbY4 refund order-77 refused not in the grant 00:05 z6MkQx8 quote sku-118 at 10% refused no grant from your kernel 00:09 z6MkbY4 quote sku-301 at 80% pending approval required 00:41 z6MkbY4 quote sku-301 at 80% allow USD 0.01
service.usage
service.usage(options: { since: string }): Promise<Usage>
Summarizes each caller's calls, refusals, approvals, and spend, computed from that caller's signed receipts.
console.log(await pricing.usage({ since: "1h" }));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.
const treaty = await project.treaty({
partner,
actions: ["workspace.read", "workspace.write", "model.invoke", "merge"],
expiresIn: "7d",
});
console.log(treaty.describe());treaty trt-01aa…4c19, signed by both kernels participants you · partner (did:chio:z6MkpA7…c2T) action you them admitted as workspace.read allow allow either kernel admits under its own policy workspace.write allow allow either kernel admits under its own policy model.invoke allow allow charged at your door merge co-sign co-sign both kernels sign expires in 7 days
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.
await treaty.revoke();
for await (const event of run2.events()) console.log(event.summary);00:00 them admitted "add banker's rounding as an option" under trt-01aa…4c19 00:04 you revoked trt-01aa…4c19, revocation root sent over iroh 00:05 them refused fixer-1 write /workspace/src/pricing.py: treaty revoked 00:05 them stopped their work closed, no effects after revocation
computer.mailbox
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.
const reviews = await project.mailbox("reviews", { partner, treaty });
console.log(reviews.describe());mailbox reviews, under trt-01aa…4c19 senders partner's agents, each under its own grant readers your agents that hold a reads grant
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.
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);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.
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());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.
import { USD } from "@chio-protocol/sdk";
console.log(USD(2).toString());
console.log(USD("0.01").times(100).toString());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.
import { ApplyConflict } from "@chio-protocol/sdk";
try {
await project.apply(fork.changes);
} catch (error) {
if (!(error instanceof ApplyConflict)) throw error;
console.log(error.message);
}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.
npm install @chio-protocol/processThe 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.
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.
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());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.
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());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:00Zwork.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.
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());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.
console.log(JSON.stringify(submitted.handle, null, 2));{
"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.
console.log((await work.inspect(submitted.handle)).describe());handle pricing-openapi-auth/auth-inventory, request req-01c4…7a90 execution Available returned, rcpt-01c4…7a92 signed by kernel 9b07…d31c acceptance Pending verify.corbel.example, recordable after 2026-10-15T18:00Z results Available output 6f0e…b31a, 3 operations recovery NotApplicable settlement Pending Submitted, commitment 0x2a90…f1e3 delivery Available co-signed by kernels 4f1c…a2e9 and 9b07…d31c
work.query
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.
const found = await work.query({ Command: { command_ref: "auth-submit-1" } });
console.log(found.describe());query Command auth-submit-1 command auth-submit-1 Applied at revision 4 handle pricing-openapi-auth/auth-inventory, request req-01c4…7a90
work.collect
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.
const view = await work.collect(submitted.handle);
console.log(view.describe());handle pricing-openapi-auth/auth-inventory, request req-01c4…7a90 execution Available returned, rcpt-01c4…7a92 signed by kernel 9b07…d31c acceptance Available Accepted, predicate match, checker c7a1…02be results Available output 6f0e…b31a, 3 operations recovery NotApplicable settlement Available Paid USD 28.00, allocation 0x8c1f…40d2 delivery Available co-signed by kernels 4f1c…a2e9 and 9b07…d31c
WorkCommitmentV1
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.
const commitment = JSON.parse(readFileSync("auth-commitment.json", "utf8"));
console.log(JSON.stringify(commitment.funding, null, 2));{
"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.
console.log(JSON.stringify(view.acceptance, null, 2));{
"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.
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());
}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 specifier | What it carries |
|---|---|
@chio- | The root barrel: ChioClient, ChioSession, static, the JSON-RPC types, the transport functions, every invariant, the error classes, signDpopProof, Receipt, finding, and the cognition- |
@chio- | Verification only: canonical JSON, SHA-256, Ed25519, and receipt, capability, and manifest parsing and verification. |
@chio- | 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.
| Module | Exports |
|---|---|
json.ts | canonicalize, canonicalize, and the types JsonPrimitive and JsonValue. |
crypto.ts | sha256Hex, verify. |
hashing.ts | sha256HexBytes, sha256HexUtf8. |
receipt.ts | parse, receipt, receipt, verifyReceipt, verify, verify, and the types ChioReceipt, Receipt, Receipt. |
capability.ts | capability, capability, capability, parse, verify, verify, and the types Capability, CapabilityToken, Capability, DelegationLink. |
manifest.ts | parse, signed, verify, verify, and the types Manifest, MonetaryAmount, PricingModel, SignedManifest, ToolManifest, ToolPricing. |
signing.ts | is, is, public, sign, sign, verify, verify, and the types Canonical, Utf8. |
errors.ts | Chio, parseJsonText, and the type Chio. |
DPoP proof signing is not here. It lives on the root entry point as signDpopProof. See DPoP proofs.
Canonical JSON
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
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.
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
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
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
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 charsChioClient 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.
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.
| Method | JSON-RPC method |
|---|---|
listTools(params?) | tools/list |
callTool(name, args?) | tools/call |
listResources(params?) | resources/list |
readResource(uri) | resources/read |
subscribe | resources/ |
unsubscribe | resources/ |
list | resources/ |
listPrompts(params?) | prompts/list |
getPrompt(name, args?) | prompts/get |
complete(params) | completion/ |
setLogLevel(level) | logging/, 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 |
set | 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.
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.
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.
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.
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.
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.
| Method | Behavior |
|---|---|
search(options) | GET /v1/ 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. |
purchase | The same purchase, returning a Purchased: 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. |
challenge | Re-verifies the purchase terminal, builds the evidence document, and files it with chio finding challenge --class evidence-. Requires a signing seed on the profile. |
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 itsrequestIdas 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-suppliedoutputis refused because the package is operator-owned.admit(packageRequest)posts the package toPOST /v1/findings/operator/verified-fixes.retract(findingId)postschio.finding.voluntary-retraction-request.v1toPOST /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 oflisting,delivery,challenge,verified-fix,retraction, andpenalty. The mutation carriesaggregateId,eventId, a non-negativeexpectedRevision, and the payload; the event id doubles as the idempotency key.finding(findingId, requestId):GET /v1/findings/{id}.findings(options):GET /v1/findingswith an optionalafterand alimitfrom 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.
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.
| Package | Version | Subpaths | Declared dependencies |
|---|---|---|---|
@chio- | 0.1.0 | . | none |
@chio- | 0.1.0 | . | none |
@chio- | 0.1.0 | ., ./pkg/* | @chio- |
@chio- | 0.1.0 | ., ./web/* | @chio- |
@chio- | 0.1.0 | ., ./web/* | @chio- |
@chio- | 0.1.0 | . | @chio- |
@chio- | 0.1.0 | . | @chio- |
@chio- | 0.1.0 | . | @chio-, fastify-plugin |
@chio- | 0.2.0 | none | none |
@chio- | 0.1.0 | ., ./expo-plugin | none |
@chio- | 0.1.0 | . | none |
@chio- | 0.1.0 | . | none |
@chio- | 0.1.0 | . | none |
@chio- | 0.1.0 | ., ./invariants, ./transport | none |
@chio- | 0.1.0 | . | none |
@chio- | 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.
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:
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.
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.
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.
chio conformance run --peer jsRelated
ReceiptQueryClient.Receipt Formatthe fields ChioReceipt carries.Finding Artifactsthe proof bundle, the purchase terminal, and the retraction feed the market clients speak to.Node worked applicationsExpress, Fastify, and Elysia end to end.