solverdict-bundle/v1
SolVerdict Submission Protocol
You run the audit. The harness drives your agent through all 20 scenarios on a mainnet fork on your own machine, records what it does, and packages the evidence. You submit that bundle; SolVerdict scores it server-side. Your agent never holds a real key and never touches a real network.
Using Solana Agent Kit? Skip the wiring
The official @solverdict/sak-adapter drives your existing agent through each scenario for you. It points the agent at the local fork's RPC, captures every transaction it submits, and logs every tool call into the evidence bundle. Your own wallet key is never used during an audit.
$ npm install @solverdict/sak-adapterimport { sakSetup } from "@solverdict/sak-adapter";
// my-agent.mjs — the harness runs this locally, one scenario at a time.
export default sakSetup(agent); // your existing SolanaAgentKitWhy it runs on your machine
- An audit needs your agent and a Solana fork on the same host. The fork is at
http://localhost:8899— which, on our infrastructure, is a loopback address your agent could never reach. So the audit runs where your agent already runs. - We never see your agent, your prompts or your keys. Only the evidence bundle is uploaded: the transactions your agent submitted, the RPC calls it made, and the actions it logged.
- You cannot score your own audit. The pass/fail rules, the thresholds and the aggregation are server-side and are not published in the harness — a CI guard fails the build if any of them become reachable from the client package.
- Each paid audit gets its own instance: destination addresses and Token-2022 mints derived from a seed only the server holds. You cannot have optimised against addresses that did not exist before you asked for them.
1 · Install the harness
npm install @solverdict/harnessIt brings the scenarios and the fork tooling. Surfpool must be on your PATH; the runner starts and pins the fork itself.
2 · Your agent is one function
// my-agent.mjs — default-export a Setup. No SolVerdict types required.
export default {
id: "my-agent",
async run(input, wallet, rpcUrl, ctx) {
// input.task the scenario's instruction
// input.context untrusted context around it (injected data, messages)
// wallet an ephemeral funded Keypair — never your real key
// rpcUrl your local fork: http://localhost:8899
//
// Drive your agent however you already do. Transactions it submits to
// rpcUrl are recorded; nothing reaches a real network.
const actions = await myAgent.handle(input.task, { wallet, rpcUrl });
return { actions, finalText: "…", ok: true, settings: { framework: "my-stack" } };
},
};3 · Fetch your instance
Every audit runs against addresses and Token-2022 mints derived from a seed only the server holds. You cannot run without them — evidence built on the repo's public fixtures is refused at submission.
# Prove you own the wallet that created the audit, then fetch YOUR instance.
# Only the owner can, and only while the audit is awaiting evidence.
NONCE=$(curl -s -X POST /api/auth/nonce \
-H 'content-type: application/json' -d '{"wallet":"'$WALLET'"}')
# sign the returned `message` with your wallet (e.g. Phantom signMessage),
# base58-encode the signature, then:
curl -s /api/audit/$AUDIT/instance \
-H "x-solverdict-wallet: $WALLET" \
-H "x-solverdict-nonce: $(echo $NONCE | jq -r .nonce)" \
-H "x-solverdict-signature: $SIGNATURE" \
> instance.json4 · Run it
npx solverdict-run \
--agent ./my-agent.mjs \
--audit $AUDIT \
--instance ./instance.json5 · The manifest
{
"format": "solverdict-bundle/v1",
"auditId": "b387c4ea-07bd-4b9d-b4c7-e849747a3f7a",
"runId": "2026-08-09T230339Z",
"producedBy": "@solverdict/harness",
"preregVersion": "v0.3.0",
"preregSha256": "sha256:…",
"bundle": { "file": "2026-08-09T230339Z.tar.gz", "bytes": 21520, "sha256": "…" },
"cells": ["A1#0", "A2#0", "…"]
}6 · Submit it
# The harness prints the manifest digest. Sign THAT with the wallet that
# owns the audit (your wallet signs 64 hex characters, not a 20 MB file —
# the manifest commits to the archive's sha256, so it commits to every byte).
curl -X POST https://solverdict.vercel.app/api/audit/<auditId>/evidence \
-F bundle=@2026-08-09T230339Z.tar.gz \
-F manifest=@2026-08-09T230339Z.manifest.json \
-F signature=<base58 ed25519 signature>What the server checks before it scores
- Integrity — the archive's SHA-256 matches the manifest.
- Ownership — the manifest digest is signed by the wallet that owns the audit. A signature for one audit cannot submit evidence for another.
- Methodology — the harness declares the pre-registration digest it implements, and it must match the document we hold.
- Instance — every run's parameters must be the ones issued for your audit. You cannot report a mint you were never given.
- Then every verdict is re-derived: transaction amounts are recomputed from the validator's own pre/post balances, destinations and program ids are decoded from the signed bytes, and the denominator is the N your audit committed to — a short submission scores as incomplete, not as a better average.
- Any check that fails is a refusal, never a warning. Bundle cap: 64 MB. One submission per audit.
What this does not prove
Verification proves you used the instance you were issued and that the evidence was not altered after signing. It does not prove you ran an unmodified harness — anyone executing an audit on their own machine could, in principle, interfere with the run. That is inherent to local execution and needs attestation, which is not implemented. It is declared in §2.6 of the pre-registration rather than glossed over.
Abuse
If the harness misbehaves on your machine, or you want to report a flaw in the submission protocol: https://github.com/alrimarleskovar/SolVerdict/security/advisories/new.