@cinchor/sdk
v0.4.0
Published
Accountability infrastructure for autonomous agents — bound what an agent can do before it acts, and prove what it did after. Verifiably enough for a regulator, auditor, insurer, or court.
Maintainers
Readme
@cinchor/sdk
Accountability infrastructure for autonomous agents. Bound what an agent can do before it acts, and prove what it did after — verifiably enough for a regulator, auditor, insurer, or court.
Auth0 for auth · Datadog for observability · Vanta for compliance → Cinchor for agent accountability.
You import a library, wrap your agent's decision and action points, and call two verbs. You do not "go on a blockchain" — Cinchor runs on a neutral, independently-governed substrate that makes the records externally verifiable, but you never manage it.
npm install @cinchor/sdkInstall — TypeScript, Python, Go
| Language | Package | Install |
|----------|---------|---------|
| TypeScript / JS | @cinchor/sdk | npm install @cinchor/sdk |
| Python | cinchor | pip install cinchor |
| Go | github.com/cinchor-hq/cinchor-sdk/go | go get github.com/cinchor-hq/cinchor-sdk/go |
The TypeScript SDK (repo root + src/) is the reference implementation. Go (go/) and Python (py/) are held to it by cross-SDK parity tests — identical capability-id and attestation-hash derivations, so a capability or record produced by one SDK is addressable and verifiable by another. All Apache-2.0.
The two verbs
enforce(action)— authorize-or-refuse a consequential action. The substrate is the enforcement point: an out-of-scope action commits no state change, no matter how the agent reasons, is prompted, or is compromised.attest(decision)— commit a tamper-evident, independently-verifiable record of a decision and the full context behind it, bound to the policy in force at the time.
Together they convert unbounded irreversible harm into bounded irreversible harm, with a record an adversary can verify.
Quickstart
import { CinchorClient, IGNIS_LOCAL } from '@cinchor/sdk';
import { Wallet } from '@omne/sdk'; // or any compatible signer
const cinchor = await CinchorClient.connect({
network: IGNIS_LOCAL,
contract: { name: 'cinchor_permissions', address: 'om1z…' }, // your deployed contract
});
// 1. A principal mints a scoped capability to an agent.
const { capabilityId } = await cinchor.mintCapability({
principal, // a Signer (the granting party)
agent: agentAddress, // om1z address of the agent
maxSpend: 100n, // spend ceiling
ttlSeconds: 3600, // expires in an hour
});
// 2. The agent enforces an action against that capability.
const outcome = await cinchor.enforce({
capability: capabilityId,
agent, // the agent Signer
amount: 40n,
});
if (!outcome.allowed) {
throw new Error(`action refused by the substrate: ${outcome.reason}`);
}
// 3. Attest a decision (provable-after).
const { attestationId } = await cinchor.attest({
capability: capabilityId,
agent,
context: { model: 'claim-triage-v3', inputs, reasoning, output },
});
// 4. Anyone can verify, without trusting the operator.
const { ok } = await cinchor.verifyAttestation({ model: 'claim-triage-v3', inputs, reasoning, output }, attestationId);What enforce returns
enforce() returns an EnforcementOutcome:
| field | meaning |
|---|---|
| allowed | true only when the substrate authorized and recorded the action |
| code | EnforcementCode — Allowed, NotFound, Revoked, Expired, OverBudget, OutOfAllowlist, Unauthorized |
| reason | the human-readable label for code |
| receipt | the on-chain commit receipt |
Unauthorized (6) is returned when the on-chain contract rejects an action whose caller is not the capability's bound agent — a caller-binding failure, not a policy failure.
A record_action receipt does not carry the contract's return code, so the verdict is classified from committed state. Classification assumes serial, single-signer use of a capability (one in-flight action at a time) — the documented integration pattern.
Verifying a receipt — verifyReceipt (0.3.0)
Verification is the half that has to work for someone who does not trust you, so it needs no client, no key, and no network:
import { verifyReceipt } from '@cinchor/sdk';
const result = await verifyReceipt(receipt, context); // positional, not an object
// result.verified — every APPLICABLE check passed
// result.checks — per-check outcome, each independently inspectable
// result.reason — first failure, for a one-line summaryThe checks form a ladder, and each rung states its own result rather than
collapsing into one boolean: the context recomputes to the committed hash, the
attestation ID re-derives, and — only if you pass an rpc — the record is shown
to be included in chain state.
Read this before relying on it. Two limits, because a verification tool that oversells itself is worse than none:
- There is no signature check yet.
OarReceiptcarries no signature or public-key field, and none of the checks verifies one. A receipt whosecontextHashandattestationIDare computed honestly — which anyone can do with this package's own exported helpers, with no key and no chain — currently returnsverified: true. These checks establish that a receipt is internally consistent and unaltered, not who issued it. The signature step is specified (OAR §3) and not yet implemented here. - Local folding does not by itself catch a lying node. The fold recomputes the Merkle path and compares it to a root the same node supplied, so it catches an inconsistent proof, not a fabricated self-consistent one. Binding the leaf to the requested storage key, and the root to an independently obtained value, are open items.
rungs.finality currently reports not implemented: validator-set signature
checking is not built yet. It is reported rather than silently skipped, because a
verifier that quietly omits a check is worse than one that admits the gap.
CLI
npx -p @cinchor/sdk verify-receipt --receipt r.json
npx -p @cinchor/sdk verify-receipt --receipt r.json --rpc https://rpc.omnechain.networkNote -p. The package declares a single bin, so npx @cinchor/sdk verify-receipt
makes npx substitute the bin for the package name and leaves verify-receipt as a
stray positional. Since 0.3.1 the CLI swallows that leading word anyway, so the
older form also works, but -p is the correct invocation.
Useful flags: --context <file> when the context is not embedded, --challenge
<nonce> to require the receipt answer a specific probe, --self-custody to
require custody_mode = self-custody, and --json for machine-readable output.
API surface
CinchorClient.connect(config)— connect to a network + deployed contract.- Lifecycle:
mintCapability,revoke,updatePolicy,allowCounterparty. - Verbs:
enforce,attest. - Audit (reads, no signer):
getCapability,getAttestation,verifyAttestation. - Verification (no client, no key):
verifyReceipt, plus theverify-receiptCLI. - Power users: the underlying
CapabilityRegistryis exposed atclient.registry, and address/attestation utilities are exported directly.
Signers
Any object exposing an om1z address and a signTransaction(tx, opts) method works (structurally typed as Signer). The Omne SDK's Wallet / WalletAccount satisfy it. The signer's address must be funded to pay gas.
Substrate
Cinchor runs on Omne, an independently-governed L1 that supplies the neutral, verifiable settlement and audit layer. The chain is the substrate, not the product — the product is bounded, provable agent authority. This package depends on @omne/sdk for signing and submission.
License
Apache-2.0. Copyright © 2026 DoneUp, Inc. See LICENSE. The client SDK is open source; the managed Cinchor gateway and service remain proprietary.
