Skip to content

ShadowKit

Documentation · ZK + agentic governance

ShadowKit is an SDK for governance where the how stays private and the outcome is verifiable. Four primitives compose:

  • Zero-knowledge votes — a Groth16 (BLS12-381) circuit proves membership and a hidden weight; identity, weight, and direction never go on-chain.
  • Timelock seal — each vote’s (direction, weight) is tlock-encrypted to the drand round at the deadline; the running tally is unknowable until close.
  • On-chain governance — a Soroban GovVault stores sealed votes, re-aggregates the revealed tally, and exposes an approval gate.
  • Bounded AI agent — an LLM plans the approved action; an on-chain policy contract rejects anything off-spec, so a hallucinating agent can never move funds wrongly.

ShadowKit ships as scoped npm packages plus the Soroban contract WASM. Browser proving + sealing path:

terminal
npm install @shadowkit/zk-prover @shadowkit/snapshot-tool \
@shadowkit/tally-reveal @shadowkit/shared

Agent + x402 services (server / Worker side):

terminal
npm install @shadowkit/agent @shadowkit/x402-shared \
@shadowkit/x402-premium-data @shadowkit/x402-api

Build a snapshot of eligible holders, generate a zero-knowledge proof, and timelock-seal the vote — all client-side. The witness never leaves the browser.

seal-vote.ts
import { buildSnapshot } from "@shadowkit/snapshot-tool";
import { generateVoteProof, nullifierFor } from "@shadowkit/zk-prover";
// 1) Merkle snapshot of eligible holders (leaf = Poseidon(Poseidon(secret), weight)).
const snapshot = await buildSnapshot([
{ secretCommit, weight: "1000" },
// ...more holders
]);
const { merklePath, pathIndices } = snapshot.getPath(myLeafIndex);
// 2) Prove + seal. direction/weight are hidden; only the 4 public signals are revealed.
const { proof, publicSignals, sealedCiphertext } = await generateVoteProof(
{
secret,
merklePath,
pathIndices,
weight: "1000",
proposalId: "0",
direction: 1, // yes — SEALED, never a public signal
merkleRoot: snapshot.root,
},
{ wasmPath: "/zk/vote.wasm", zkeyPath: "/zk/vote_final.zkey" },
deadlineUnixSeconds, // tlock round derived from this
);
// publicSignals = { merkleRoot, nullifier, proposalId, sealedCommitmentHash }
// → submit GovVault.cast_vote(proposalId, proof, publicSignals, sealedCiphertext)