@inveigo/cli
v0.3.0
Published
Inveigo operator CLI — thin wrapper over @inveigo/sdk
Readme
@inveigo/cli
inveigo — operator CLI for the Inveigo program (AI Bounty Court). A thin
wrapper over @inveigo/sdk: every on-chain instruction
can be called manually, and every account the instructions need is derived
from on-chain state unless you override it.
Setup
npm i -g @inveigo/cli # global `inveigo` command (published package)
npx @inveigo/cli <cmd> # or ad-hoc, no install
# from the repo instead:
pnpm --filter @inveigo/cli build # tsc typecheck + tsup bundle → dist/
pnpm --filter @inveigo/cli dev … # or: bun bin/dev.js … (runs TS sources)- RPC:
--rpc/$INVEIGO_RPC_URL(defaulthttp://127.0.0.1:8899) - Wallet:
--keypair/$INVEIGO_KEYPAIR_PATH/$ANCHOR_WALLET(default~/.config/solana/id.json). The wallet is fee payer and the default signer on every instruction. - Evidence daemon:
--evidence/$INVEIGO_EVIDENCE_URLonthesis-publish/challenge/monitor(default pairs with--rpc: devnet RPC →https://api.devnet.useaccord.xyz, elsehttps://api.useaccord.xyz; localnet has no default — pass one). - Every chain command takes
--dry-run(build + print the instruction, no send),--json(one JSON object for jq), and--quiet.
Commands
| Command | What it does |
|---|---|
| config:show | Resolved rpc/ws/keypair/program ids + payer balance |
| config:balance [--address] | SOL balance of the wallet or any address |
| global:show | Decoded GlobalState (authority, protocol fee) |
| item:show --item <PDA> | Decoded boss: pot, state, challenger, expiry, … |
| initialize --protocol-fee-bps <bps> [--juror-credential --juror-schema] | One-time GlobalState init (signer = authority); the SAS keys are the juror gate courts must match (ADR-0012) |
| create-item --mint --claim-hash --seed --timeout-duration --creator-share-bps | Create a boss; seed escrowed into the vault ATA; pins the mint's registered court |
| challenge --item (--evidence-hash <hex> \| --body-file <path>) | Attack: bond + fee → Accord dispute (item → Challenging); --body-file also hashes the payload and publishes it (ECIES to the evidence operator) once the dispute exists |
| settle --item | Permissionless verdict crank (flip payout / hold → pot / refund) |
| evidence-publish (--item <PDA> \| --dispute <PDA>) --body-file <path> | Deliver/repair an attack's evidence: ECIES to the court's operator + POST to the daemon (idempotent — safe to retry) |
| monitor --item [--watch] [--crank] | Adjudication status: dispute phase + countdown, drawn jury, commit/reveal progress, evidence-on-daemon check, verdict → next crank; --crank fires it (settle/timeout) instead of reporting |
| timeout --item | Harvest crank after expiry (creator takes the pot) |
| withdraw-item --item | Creator-signed early withdrawal of an Active item |
| thesis-publish --mint --title --body-file | Upload the inveigo-thesis-v1 manifest to the evidence daemon; prints the claim hash for create-item |
| register-subaccord --subaccord <PDA> | Pin/overwrite the court for its fee mint (authority-only, ADR-0012) |
| token:create-mint --mint-keypair <path> [--decimals] | Bootstrap a test mint (createAccount + initializeMint2) |
| token:create-ata --mint [--owner] | Idempotent canonical ATA create |
| token:mint-to --mint --dest --amount | Mint test tokens (mint authority signs) |
Court provisioning (ADR-0012 operator story)
| Command | What it does |
| court-create --mint [flags] | Provision a Subaccord court — arena-profile defaults, staking+fee = --mint (ADR-0004). Every parameter is a flag (fees, stakes, windows, panel, aggregation — see Customizing the court profile); --domain-doc <path> / --domain-ref <hex> / --salt pick the domain_ref |
| juror-stake --subaccord [--amount] [--attestation] | Stake a juror into a court; Merkle proof built from on-chain state; attestation required iff the court is SAS-gated |
| sas:create-credential --name | Create the SAS credential (authorized signer = this wallet) |
| sas:create-schema --credential [--name] | Create the juror schema — layout [4,4] (two U128), load-bearing: Accord reads the juror wallet at data[0..32] |
| sas:attest --credential --schema --juror [--expiry] | Issue a juror attestation for a jurat AI-agent wallet (nonce+data = the juror) |
Customizing the court profile
Every Subaccord parameter is a court-create flag — the defaults below are
the arena profile (N=1, max_appeals=0, Plurality, depth 12). All amounts
are raw base units; all windows are seconds.
Fees & economics:
| Flag | Default | Sets |
|---|---|---|
| --fee-per-juror | 100000000 | Per-juror fee. The filing fee challengers pay is read live from the item's court at challenge time: (min_jury_size + 1) · fee_per_juror (ADR-0030) — with the default N=1 panel that is 2 · fee_per_juror |
| --min-stake | 3000000000 | Juror draw-eligibility threshold |
| --alpha-bps | 1000 | Slash factor for an incoherent juror (bps of min_stake) |
Keep slash-dominance when touching these: α · minStake / 10⁴ ≥ 2 · feePerJuror
— a juror's worst-case slash must cover the fees it could steal. The defaults
sit exactly on the line: 0.1 · 3e9 = 3e8 ≥ 2 · 1e8. Lowering --min-stake
or --alpha-bps, or raising --fee-per-juror, past that line breaks the
honesty game.
Time windows:
| Flag | Default | Sets |
|---|---|---|
| --review-window | 86400 (1d) | Evidence-review phase before commit opens |
| --commit-window | 43200 (12h) | Commit phase |
| --reveal-window | 43200 (12h) | Reveal phase |
| --appeal-window | 86400 (1d) | Appeal window after a round resolves |
Panel & the rest: --min-jury-size (odd; 1 = single juror, the Inveigo
default), --max-appeals, --reveal-threshold-bps (6666 = 2/3 quorum),
--max-draw-attempts, --depth (accumulator tree; pool ≤ 2^depth),
--aggregation plurality\|median, --coherence-tol-bps (Median courts
only; 0 = exact), --authority, --evidence-operator. Fixed by law:
one token for staking + fees (--mint, ADR-0004), shortfallPolicy =
Redraw (Accord v1's only rule), and the evidence spec
(inveigo:evidence-v1, shared by every court).
Swapping a mint's court — e.g. for lower fees
Courts are never modified — not even their fees. Accord has a 48h
timelocked update path, but Inveigo's operator policy is swap-only: items
pin their court at creation (item.subaccord, ADR-0012), so mutating a
live court would silently reprice open bosses mid-fight. Change terms by
provisioning a fresh court and re-pinning the mint:
NEW_COURT=$(inveigo court-create --mint $MINT \
--juror-credential $CRED --juror-schema $SCHEMA \
--fee-per-juror 25000000 \
--domain-doc apps/cli/assets/inveigo-domain.md \
--quiet)
inveigo juror-stake --subaccord $NEW_COURT --keypair agent.json --attestation $ATTEST
inveigo register-subaccord --subaccord $NEW_COURT(--fee-per-juror 25000000 is the whole point: the new court's filing fee
becomes 2 · 25_000_000 instead of 2 · 100_000_000.)
What happens:
register-subaccordoverwrites the["arena-court", mint]entry (authority-only). Every item created afterwards pins the new court and pays its filing fee — the fee is read live from the item's court at challenge time, never from the registry.- Existing items keep their old court for life — old bosses keep paying the old fee. Keep the old court armed (its juror staked) until its items are flipped, timed out, or withdrawn; then it can be abandoned.
- A fresh Subaccord starts with zero stakers — re-stake the jurat agent into the new court (step above) before the first challenge, or disputes fail to draw.
- The new court must bind the same SAS keys
initializepinned (--juror-credential/--juror-schema;SubaccordSasMismatchotherwise). - The PDA must differ from the old court's. The Subaccord PDA is
["subaccord", creator, domain_ref]— same creator + same domain doc = same PDA, and the create collides. Rotate one of them: mint+salt courts pass a new--salt; domain-doc courts bump the doc (any edit — e.g. a revision line — changes its hash, the ref, and the thesis namespace; old theses stay fetchable by their bare content hash).
Derived defaults
challenge: bond =item.seed(ADR-0011), fee = the subaccord's live filing fee(min_jury_size + 1) · fee_per_juror(ADR-0030), subaccord =item.subaccord, dispute = Accord PDA["dispute", challenger, GlobalState.dispute_seq](ADR-0012 — program-wide monotonic nonce, so re-challenges never collide), fee vault = subaccord's ATA.settle: dispute =item.current_dispute; challenger/creator/authority payout ATAs = canonical ATAs of the on-chain parties.timeout/withdraw-item: every account derives from the Item + GlobalState.
Override any of them with the corresponding flag (--bond, --fee,
--subaccord, --dispute, --fee-vault, --creator, --authority,
--item-vault, --creator-ata, --authority-ata, …).
Zero to your first boss — the full CLI walkthrough
Everything below is one linear lane, executed verbatim in this order on a
fresh local net. One wallet (the CLI default, ~/.config/solana/id.json)
plays every operator role: SAS credential authority, court authority,
GlobalState authority, and — for the demo — the thesis creator. AI agents
are the only possible jurors: courts are born SAS-gated, only attested
wallets can stake, and register-subaccord refuses courts not bound to the
keys initialize pinned (ADR-0012).
Point the solana CLI at the local net first (it defaults to whatever
solana config get says — sending "local" funding txs at a remote cluster
is an easy mistake):
solana config set --url localhost0. Validator
make run_validator # inveigo + accord + SAS .so loaded; SAS comes from the
# vendored ../solana-attestation-service (cargo build-sbf
# there first if the .so is missing)1. The SAS gate (what initialize will pin)
CRED=$(inveigo sas:create-credential --name juror-agents --quiet)
SCHEMA=$(inveigo sas:create-schema --credential $CRED --quiet)CRED is the attestation key: your wallet is its only authorized signer, so
only you can issue juror attestations. SCHEMA's layout is fixed
([4,4] — two U128s) because Accord reads the juror wallet at
data[0..32] at a fixed offset.
2. Mint + court
solana-keygen new -o mint.json --no-bip39-passphrase --force
MINT=$(solana-keygen pubkey mint.json)
inveigo token:create-mint --mint-keypair mint.json
COURT=$(inveigo court-create --mint $MINT \
--juror-credential $CRED --juror-schema $SCHEMA --quiet)court-create provisions an Accord Subaccord with the arena profile
(N=1, max_appeals = 0, Plurality, depth 12) with staking + fee token both
$MINT (ADR-0004). --salt <n> derives a fresh court PDA for swaps. Prefer
--domain-doc apps/cli/assets/inveigo-domain.md: the court's domain_ref becomes sha256 of the
domain document jurat reads as its standing instructions, and the evidence
daemon serves the theses of this court under that ref
(/domains/{domain_ref}/{claim_hash}). After register-subaccord, publish
the document once: PUT /domains/{domain_ref}?subaccord=$COURT with the
file bytes on the daemon that hosts the court's evidence.
inveigo initialize --protocol-fee-bps 1000 \
--juror-credential $CRED --juror-schema $SCHEMA
inveigo register-subaccord --subaccord $COURT
inveigo global:showinitialize is one-time and pins the SAS keys; register-subaccord
publishes the court for the mint — from here create-item on $MINT
structurally cannot run without a registered court.
4. Arm a jurat AI juror
The juror is a separate keypair the agent owns. Attest it, fund it (≥ minStake tokens plus a little SOL for fees), then the agent stakes:
solana-keygen new -o agent.json --no-bip39-passphrase --force
AGENT=$(solana-keygen pubkey agent.json)
ATTEST=$(inveigo sas:attest --credential $CRED --schema $SCHEMA --juror $AGENT --quiet)
AGENT_ATA=$(inveigo token:create-ata --mint $MINT --owner $AGENT --quiet)
inveigo token:mint-to --mint $MINT --dest $AGENT_ATA --amount 3000000000
solana transfer $AGENT 1 --allow-unfunded-recipient
inveigo juror-stake --subaccord $COURT --keypair agent.json --attestation $ATTESTWithout the attestation the stake is rejected — that is the AI-only gate.
The court is now armed (staker_count ≥ min_jury_size) and disputes can
resolve.
5. The first thesis
The claim text lives off-chain (evidence server); on-chain carries only its sha256. Fund the creator (your demo wallet), then publish the thesis and create the boss:
CREATOR_ATA=$(inveigo token:create-ata --mint $MINT --quiet)
inveigo token:mint-to --mint $MINT --dest $CREATOR_ATA --amount 10000000000
printf 'your controversial thesis here' > thesis.md
CLAIM=$(inveigo thesis-publish --mint $MINT --title "My thesis" \
--body-file thesis.md --quiet)
ITEM=$(inveigo create-item --mint $MINT --claim-hash $CLAIM --seed 5000000000 \
--timeout-duration 3600 --creator-share-bps 2500 --quiet)
inveigo item:show --item $ITEMthesis-publish PUTs the inveigo-thesis-v1 manifest as a sub-document of
the court's domain_ref (chain-gated: the court must be registered) and
prints the sha256 of the FULL manifest — always feed create-item the hash
this command printed, never a hand-hashed body.
item:show should read state: 0 (Active), pot: 5_000_000_000, and
subaccord = $COURT — the boss is live, pinned to its court for life,
and the seed is escrowed in the item's vault ATA.
6. Attacking it (optional, same lane)
printf 'your winning argument' > attack.md
inveigo challenge --item $ITEM --body-file attack.md
inveigo monitor --item $ITEM --watch
# the jurat agent draws, reviews the evidence, commits and reveals (its job,
# outside this CLI); monitor prints HOLD/FLIP/REFUND when the dispute lands
# Final/Failed, then:
inveigo settle --item $ITEM
# …or let the monitor be the keeper: it fires the settle/timeout crank itself
# the moment it is ready (exits after one lands):
inveigo monitor --item $ITEM --watch --crankchallenge --body-file hashes the payload into the on-chain commitment and,
once the dispute exists, ECIES-encrypts the same bytes to the court's
evidence operator and publishes them to the daemon — until that bundle is
stored the drawn jury cannot read the argument (monitor shows
evidence: missing). --evidence-hash remains for pre-committed payloads;
combine it with a manual daemon POST if you need the raw path.
Note: settle and timeout pay three distinct token accounts
(challenger/creator/authority); the program rejects duplicate mutable
accounts, so a real fight needs the roles on separate wallets.
Tests
pnpm --filter @inveigo/cli test # vitest: arg parsing, wallet resolution,
# output renderers, error mapping, command suite