Integrations
Karatum Trading Assurance for Cancore bots
On this page
All documents
Methodology
- Agent Honesty Index (AHI) — methodology
- Verifiable judge: who checks the checker?
- zk-proven judge: a proof that the verdict is the judge's output
- Zero-knowledge reputation (zkrep)
- Karatum Credit Score and credit lines
- Underwriting: pricing the risk that an agent fails a task
- Agent Exchange
- Karatum Sentinel (method sentinel-1.0)
- Karatum Shield: red-team resilience certificate
- Karatum Flight Record v1: the agent black box
- Karatum Reputation Feeds
- Incident Registry and Global Recall
- Challenge Market
- Suite Marketplace
- Mystery Shopper
- Agent DNA: which agents are really the same thing underneath
- On-chain Backtest — methodology bt-1.0
- Protocol Agent-Readiness Audit
- Compliance Evidence Pack
- Karatum Trading Assurance: three tracks for a trading agent
Specifications
Integrations
- Karatum for Minds agents and Moca AIR agentic commerce
- Karatum agent scores as Chainlink-style data feeds (testnets)
- ENS: <build>.karatum.eth (Ethereum Sepolia, testnet)
- Karatum certificate status over Hyperlane (testnets)
- Karatum × Lit Protocol: only certified agents may decrypt or act
- Karatum Trading Assurance for Cancore bots
- Minds, Moca AIR and Moca Chain: what builders can use (research, 2026-10-16)
Source files
Status: prototype. Built from Cancore's public SDK source and documentation. There is no partnership with Cancore, Cancore has not reviewed or endorsed this, Karatum has no Cancore account, and it has not been run on the log of a live Cancore bot: every log in the tests is synthetic. The one outside input is a ten-entry log written by Cancore's own audit.ts on our machine, used to check that our hashing matches theirs.
Code: packages/trading (@karatum/trading). Method: trading-assurance.md.
What Cancore gives us to read
Cancore is a trading venue on Canton Network and EVM chains. An AI agent trades there through @cancore/bot-mcp, which talks over a local unix socket to cancore-botd. The daemon holds the key of one bot account, checks every trade command against limits the owner set (default-deny), and writes each decision to audit.jsonl, a hash-chained, append-only log. Both packages are release candidates (0.1.0-rc.0), built from source and not on npm.
Everything below was read in Cancore's SDK repository, github.com/Cancore-io/sdk (Apache-2.0), at commit 79f804add8c5d13dca4ebc7a70a8fc2026a0cc0a:
| What | File in their repository |
|---|---|
Audit entry: seq, at, method, riskClass, decision, reason?, params?, prev, hash | packages/botd/src/types.ts (AuditEntry) |
Hashing: hash = sha256 over sorted-key JSON of the entry without hash; prev = previous hash, 64 zeros at seq 0 | packages/botd/src/audit.ts (hashEntry, verify) |
Methods, their risk class (read, reduce, trade, owner), parameter schemas, deny reasons, error codes | packages/botd/src/protocol/index.ts (PARAMS, RISK_CLASS, DENY_REASONS, ERROR) |
Limits file schema (allowedPairs, maxOrderAmount, maxOrderNotional, maxDailyVolume, maxPosition, maxOpenOrders, maxPriceDeviationBps, maxCommandsPerMinute, perOrderUsd, perDayUsd, usdCapsOptOut) | packages/botd/src/limits.ts |
| The gate itself, check by check | packages/botd/src/riskgate.ts |
Which entries the daemon writes and when: the decision before it acts, a second error entry when acting fails, settle entries from the settlement loop, forbidden for an owner method on the agent socket | packages/botd/src/daemon.ts |
ordersPerHour (default 5) lives in the daemon's config, not in the limits file | packages/botd/src/config.ts, riskgate.ts |
| Order shape and order endpoints | packages/client/spec/openapi.json (OrderResponseDto, /orders, /orders/my, /orders/{id}) |
API hosts: mainnet api.cancore.io, testnet api-testnet.cancore.app, devnet api-dev.cancore.app | packages/client/src/dvp-admins.ts |
Three properties of the log shape everything that follows:
- An
order.makeentry has no order id. The daemon writes the entry before it calls the API and logs no result, so the id the venue assigns is in no entry of that order.order.takeandorder.canceldo carry the id, and so dosettleentries. - The log records the command and the decision, not the state the decision was taken on. Reference prices, USD prices, positions and the open-order book are not in it.
- The chain has no key and no outside witness. Cancore's docs say that
audit verifydoes not catch a log cut at its end, and that the chain catches an edit, not a full replacement. The daemon catches a cut only against its own state file.
What each track shows, and what it does not
| Track | Shows | Does not show |
|---|---|---|
| Log integrity (precondition for the two verdicts) | No entry between the first and the last was edited, removed, inserted or reordered. With an anchor (a chain head recorded earlier outside the operator's control): the log up to that head was not cut or rewritten since. | Without an anchor: that the end of the log was not cut off, or that the whole file was not rewritten with fresh hashes. That the daemon that wrote it was unmodified. |
| Execution Reliability | For every take and cancel the daemon answered with success: the final state the log shows for that order id (settled, cancelled, expired, taken_by_other, ...), a cancel that answered "cancelled" and then filled, a take with no final state long after. For made orders: how many reached a final state and how many did not. | From the log alone, which made order did not settle (no id in the make entry). What happened to orders open at a pause, orders.cancelAll or strategy.stop, which cancel without naming orders. That "settled" is true on the ledger: it is the daemon's reading of the venue. |
| Risk Compliance | Every command recorded as allowed that the limits in force refuse: pair not allowed, order amount or notional over its cap, a cap that is missing (default-deny), USD caps missing, a trade while paused or locked, and, as certain lower bounds, commands per minute, orders per hour, open orders and daily volume. Refusals counted by reason. Owner-only methods (withdraw, limits.set, resume, unlock, ...) requested on the agent socket and refused: bot-mcp has no such tools, so this is a prompt-injection or compromise signal. | The price corridor, USD value per order and per day, and net position: they need the prices and positions the daemon saw, which it does not log. Takes beyond rate and pause, unless order details are supplied. Anything done on the account outside the daemon. Which socket an allowed owner command came on. |
| Strategy Performance (descriptive, never a verdict) | Number of fills and the USD turnover the daemon recorded at fill time. With order details: per pair, base bought and sold, average prices, realised PnL and its largest drawdown. | Anything about the future. Unrealised PnL, fees, holdings from before the log. It is not a promise of returns and it is not read by the other two tracks. |
The verdict statuses, scores and evidence classes are defined in the methodology.
Run it on your own botd log
Nothing leaves your machine: the CLI reads local files and makes no network call.
# copies, not the live files (the daemon appends to audit.jsonl)
sudo cp /var/lib/cancore-bot/audit.jsonl /var/lib/cancore-bot/limits.json . && sudo chown "$USER" audit.jsonl limits.json
# 1. the chain, re-walked independently of cancore-bot (read-only; exit 0 intact, 3 broken)
pnpm exec tsx packages/trading/src/cli.ts verify-log audit.jsonl
# 2. the three tracks, as text
pnpm exec tsx packages/trading/src/cli.ts evaluate audit.jsonl --limits limits.json --orders-per-hour 5
# 3. the sealed report, and the part of it that may be published
pnpm exec tsx packages/trading/src/cli.ts report audit.jsonl --limits limits.json --out report.json
pnpm exec tsx packages/trading/src/cli.ts report audit.jsonl --limits limits.json --public --out report.public.jsonOptions:
| Option | Meaning |
|---|---|
--limits <file> | The daemon's limits.json. If the log holds limits.set entries (the owner CLI's limits set writes one, with the limits in it), the limits in force at each moment come from the log and the file is only compared with the last of them. |
--orders-per-hour <n> | ordersPerHour from the daemon's config.json. Without it the daemon's default of 5 is assumed and a breach is reported as major, not as a violation. |
--anchor <seq>:<hash> | A chain head you recorded earlier (the chain head line of an earlier run). The log must still contain that entry. Repeatable. |
--orders <file> | Order states you exported, see below. |
--stale-after-hours <n> | An order with no final state this long after its claim is stale. Default 24; Karatum's threshold, not a Cancore rule. |
--clock-tolerance-ms <n> | Slack for the rate windows: the daemon reads its clock, fetches prices, then writes the entry. Default 5000. |
Exit codes: 0 no track failed; 1 Execution Reliability or Risk Compliance is FAILURE; 2 usage or unreadable input; 3 the log does not verify.
To try it without a bot, the synthetic fixtures are in packages/trading/fixtures/cancore/ (manifest.json says what each one is):
F=packages/trading/fixtures/cancore
pnpm exec tsx packages/trading/src/cli.ts evaluate $F/synthetic-overlimit-allowed.audit.jsonl --limits $F/limits.jsonOrder states (optional)
An export of order states lets the adapter name the made order that did not settle, replay takes in full, and compute PnL. The file is { "orders": [...], "ownOrderIds": [...] }, where orders are OrderResponseDto objects as Cancore's API returns them and ownOrderIds are the ids of the orders the bot made itself.
There is no credential-free way to get them. On devnet (https://api-dev.cancore.app, six unauthenticated GETs on 2026-11-06 21:17 UTC):
| Endpoint | Answer |
|---|---|
GET /livez, /trading-pairs, /htlc/fee-config, /prices | 200, public |
GET /orders, GET /orders/{id} | 401 |
The OpenAPI spec marks /orders, /orders/my and /orders/{id} as bearer-only. The spec also lists GET /public/activity?partyId= (completed swap count and USD volume of a party) as public; we did not call it, having no party of our own to ask about. So order states need a session of the bot's own account, which is opened by signing a challenge with the bot's key. Karatum does not hold that key and should not. The owner exports the JSON with their own session, or a later live reader runs on the owner's machine. The code is a port (OrderStateSource in packages/trading/src/cancore/orders.ts) with one implementation that reads a file; a file is the owner's word, like the log, so it adds detail and does not raise the evidence class.
Privacy
Canton is privacy-preserving: a trade is visible to its parties, not to the public. The assurance keeps that property.
- The audit log, the limits file and any order export stay with the owner. The CLI runs locally.
- The full report (
report) quotes parameters in its findings and is for the owner. - The public report (
report --public) is what Karatum would publish or attest: the two verdict statuses and scores, counts, finding codes with the positions they refer to, the chain head, the sha256 of the log file and of the limits, and the hash of the full report. No pairs, amounts, prices, order ids, parties or per-entry hashes, and nothing from the performance track beyond the number of fills. - The chain head and file hashes are commitments to low-entropy data without a salt. Confirming a guess would take the entire log, entry for entry; that is not practical for a real log, and it is a property to know about before publishing a head for a very short one.
Nothing is published today: the report format exists, the attestation of it does not.
Limits of this integration
- Not run on a real bot. The parsing, hashing and replay follow Cancore's source at one commit of a release candidate. A later botd may change an entry or a rule;
packages/trading/src/cancore/protocol.tsnames the files to re-read, and the test against the log written by Cancore'saudit.tsfails first if the hashing changes. - The log is the operator's. An operator who controls the machine controls the log, the limits file and the daemon binary. Anchoring a head limits what can be rewritten afterwards; it does not make the daemon honest.
- The reconciliation of made orders from the log alone is by count, because the log has no id to reconcile by.
- The matching of made orders against an order export assumes the venue stores the amounts as the daemon sent them (base amount exact, quote amount within one part in a million). That has not been checked against a real order.
- Evidence class from a log alone is E1. Karatum's default certification policy asks for E3, so a log-only report is not certificate-eligible. See the methodology for what would raise it.
What we would need from Cancore
See the one-page proposal: docs/investor/cancore-proposal.md.