Audit guide
Three ways to check a verdict yourself: with any agent, with Claude Code, or with the public ov command line.
Auditing a verdict needs no key, no wallet, no database and no permission. Every input is public: Sui JSON-RPC, the Walrus aggregator, GonkaRouter's public receipts and the app's own read routes. Pick whichever of the three paths below fits the tool in front of you.
1. With any agent
https://app.openverdict.info/llms.txt is written for a model. It carries the
protocol summary, the routes an audit reads, the third-party sources to check
them against, and the three recomputations that decide whether a record is
intact. Point any capable agent at that file with a claim link and it has
everything it needs.
The three recomputations, verbatim from that file, are the whole of the cryptographic argument:
- Vote commitment.
blake2b256(BCS(VotePreimageV1 { claim_id, agent_profile_id, jury_seat_id, phase u8, outcome u8, confidence_bps u16, evidence_root, output_hash, run_hash, salt })), in that order, must equal the commitment Sui stored before the reveal. The salt is published nowhere: it is the fifth argument of thereveal_votetransaction. - Run hash.
blake2b256(BCS(RunRecordV1 { run_id, claim_object_id, agent_profile_id, jury_seat_id, phase, attempt, provider_id, model_id, gonka_request_id, prompt_hash, input_hash, output_hash, tool_transcript_hash, evidence_root, requested_at_ms, completed_at_ms }))must equal the run hash in the run approval on Sui. - Truth Score. Each valid final-round reveal becomes a probability in
basis points (YES its confidence, NO 10000 minus it, UNSURE 5000), and the
score is
(sum + floor(N / 2)) / Nin integer arithmetic. It must equal the score on the Resolution Certificate.
The routes an audit reads
| Route | What it gives |
|---|---|
GET /api/claims/<claimId> | State, phase, deadlines, attempt |
GET /api/claims/<claimId>/report | The verdict, the final-round votes and the auditBundle: committee, evidence roots per phase, runs, run approvals, commitments, reveals and certificate. This is the spine of an audit. |
GET /api/claims/<claimId>/runs/<runId>/proof | One juror run, its hashes, its sealed and revealed Walrus blob ids, and the Sui objects behind it |
GET /api/claims/<claimId>/events?snapshot=1 | The resolution event log. Drop snapshot for the live Server-Sent Events stream. |
GET /api/gateway-receipts/<gatewayRequestId> | GonkaRouter's public receipt, proxied only because the gateway sends no CORS headers |
The sources that are not ours
- Sui JSON-RPC at
https://sui-testnet-rpc.publicnode.comorhttps://fullnode.testnet.sui.io:443. - The Sui explorer at
https://suiscan.xyz/testnet/tx/<digest>and/object/<id>. - Walrus at
https://aggregator.walrus-testnet.walrus.space/v1/blobs/<blobId>. - GonkaRouter at
https://api.gonkarouter.io/v1/receipts/<gatewayRequestId>. A 404 means no record and a 429 means rate limited. Use thereq-...id, not thedevshard-...one.
2. With Claude Code
The repository ships a skill, .claude/skills/openverdict-audit/. Install it
once with a symlink:
ln -s "$PWD/.claude/skills/openverdict-audit" ~/.claude/skills/openverdict-audit
Then paste a claim link, a report link, a run link, a queue link or a bare 0x
id into the session, or ask in plain words: "audit this", "verify this claim",
"is the jury healthy", "watch this claim". The skill runs the public auditor,
reads the Markdown dossier it produces, and answers questions from it.
It presents in three tiers. The verdict card and a short narrative come first. Asking about a specific juror moves to the research trail for that seat. Asking for everything prints the full trail with the pinned system prompt and every message verbatim.
The skill is read-only by construction. It runs nothing but the two launchers in its own folder and read-only requests, its only writes are the audit outputs in a scratch directory, and it never submits anything without an explicit go from you. It needs network access and nothing else: no key, no database, no wallet.
3. With the ov command line
ov is the public CLI. After pnpm install it needs no API key, no wallet and
no database, because it reads and writes only the public API.
pnpm install
pnpm ov help
By default it talks to https://app.openverdict.info. Point it elsewhere with
--base <url>.
Global options
| Option | Meaning |
|---|---|
--base <url> | Another deployment |
--json | Machine output on stdout, one JSON document, NDJSON for watch. For audit only, --json takes a file path rather than being a switch. |
--no-banner | Skip the banner, or set OV_NO_BANNER=1 |
--no-color | No colour codes in the banner |
--timeout <duration> | Per request timeout. Durations accept 30s, 9m, 1h. |
The banner is written to stderr, so --json on stdout stays parseable.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 2 | Input or request error, with one error: line on stderr |
| 3 | The claim voided or gave up (watch) |
| 4 | watch stopped before the end, its timeout or budget reached |
| 5 | Rate limited or writes disabled (submit, extract) |
ov audit uses its own set: 0 when every check passed or was unavailable,
1 when any check failed, 2 on an input or fetch error.
The commands
ov weather
Is the jury healthy? One line per model family (DeepSeek, MiniMax, Kimi and web search), then clear or not clear. Not clear means new submissions queue until all four families answer a probe.
JSON shape: { probedAtMs, stale, clear, families[] }, where each family is
{ modelId, family, ok, latencyMs, status }.
ov board [--limit <n>]
The public board: every claim, newest first, with state, result, score and attempt. The limit is a whole number from 1 to 200 and defaults to 50.
JSON shape: { claims: [{ claimId, link, state, stateLabel, statement, result?, truthScoreBps?, attempt? }] }.
ov extract (--url <url> | --text "<text>" | --file <path>)
Extract up to three checkable claims from a page or a paragraph. Exactly one of
the three inputs is required. Text runs 40 to 20000 characters and a URL must
be http: or https: and at most 2048 characters.
JSON shape: { claims: [{ claim, reason, quote }], language, claim, sourceUrl, modelId, gonkaRequestId, gatewayRequestId }.
Exit 2 when no checkable claim was found or the page could not be fetched, exit 5 when writes are disabled or you are rate limited.
ov submit "<claim>" [--text "..."] [--url <https url>]... [--criteria "..."]
Submit a claim to the jury. The statement is 5 to 1000 characters, the optional
context text up to 20000, up to five https: URLs, and criteria up to 2000
characters. The public rate limit is five submissions per minute.
A 200 means the jury is forming and prints the claim id and its link. A 202 means the weather was not clear and the submission is queued; it prints the queue id, the weather block, and the note that queued items expire after six hours. Both exit 0.
JSON shape: the response body plus link and kind, where kind is "claim"
or "queued".
ov queue <queueId or link>
A queued submission: QUEUED, LAUNCHED with its claim, EXPIRED or CANCELLED, plus the weather.
JSON shape: { queueId, status, statement, createdAt, expiresAt, weather },
plus claimId when it launched and launchError when a launch failed.
ov status <claim id or link>
One block: the statement, the state in plain words, seats committed and revealed, the attempt, the next deadline and the result. A short hex prefix is resolved through the board, and an ambiguous prefix exits 2 with the candidates listed.
JSON shape: the full claim inspection, with claimId, mode, state,
statement, resolutionCriteria, deadlines, committeeId,
evidenceRoots, commitments, rounds, attemptChain and result, plus
deliberation and debateConvergedAfterExchange on a two-round claim.
ov watch <id or link> [--for <duration>] [--since <sequence>] [--verbose]
Follow a verification live, one dated line per step, until it ends or --for
runs out. The default is nine minutes, chosen because a Claude Code tool call
cannot exceed ten. --since resumes from a sequence number and --verbose
adds research tick lines.
It polls a queue every thirty seconds until it launches, then follows the Server-Sent Events stream, with a sixty-second poll of the claim alongside to catch a void or a give-up the stream does not carry. It reconnects up to five times with backoff.
JSON output is NDJSON: one line per event, then a summary object with kind
(always watch_summary), claimId, queueId, state, stateLabel,
lastSequence, exitCode, result, attemptChain and reason.
Exit 0 when the claim finalized, 3 when the attempt voided with no relaunch inside the window or the verification gave up, 4 when the window ran out first. The last line tells you the sequence to resume from.
ov audit <claim id or link> [--json <file>] [--out <file>] [--run <runId>] [--quiet] [--trace]
Rebuild and check the whole public record of a verdict. Accepts a claim link, a
report link, a run link, a queue link, a bare 0x id or a hex prefix.
--json <file>writes the JSON dump to that path.--out <file>writes the Markdown dossier, defaulting to.audit/<claimId>.md.--run <runId>narrows to one juror run.--quietprints only the verdict card to stdout while the full dossier still goes to the output file.--traceappends the research trail, with--juror <n>,--round 1|2and--full.
The dossier headings are fixed: verdict card, timeline, jury, votes and commitments, juror runs, debate and round two on a two-round claim, truth score, certificate on Sui, what this audit proves and what it does not, and data.
The JSON document is version 1 with these top-level keys: version,
generatedAt, target, status, claim, verdict, queue, jury,
votes, runs, claimChecks, timeline, timelineSource, debate,
score, certificate, urls, sources, summary and exitCode. status
is one of FINALIZED, IN_PROGRESS, VOIDED, GAVE_UP, CANCELLED or QUEUED, and
summary counts passed, failed, unavailable and skipped checks by group.
ov trace <claim id or link> [--juror <n>] [--round 1|2] [--full]
The research trail: every juror's searches, opened pages, quotes, answer and
gateway receipt, turn by turn. --full adds the pinned system prompt once with
its hash, the claim JSON the juror received, every message verbatim including
page texts, and the raw completion.
JSON shape: { claimId, statement, jurors: [{ jurorIndex, modelId?, role?, rounds: [{ phase, runId, kind, vote?, missing?, turns[] }] }] }.
Pointing it at a queued submission is an error: there is no jury yet.
The sibling script
pnpm audit:claim runs the same auditor without the banner, and adds
--list for the board:
pnpm audit:claim <link|id> [--base <url>] [--json <file>] [--out <file>] [--run <runId>] [--quiet]
pnpm audit:claim --list [--base <url>] [--limit <n>] [--json <file>]
Reading the result
Only a FAIL row is blocking. UNAVAILABLE means a public source did not
answer and carries a manual URL so you can check it by hand; SKIPPED means
the check does not apply to that run, which is normal for the research-only
checks on a table vote. The check ids themselves are explained on the
trust model page.
If every reachable check passes, the record supports the certificate. If any check fails, the public record does not fully support the certificate, and the failing rows are the place to look.