Docs12 sectionsLast checked 2026-11-11

How Shroud works, in full

Shroud is zero-knowledge privacy for AI agents on Solana. Trading agents have the most copied wallets on chain: anyone can watch what they buy, follow them in, and trace a fresh wallet back to the one that funded it.

A dot on this page means

Public, anyone can see it on chain

Private, it stays with you

Program id, on Solana mainnet

D2XCVLhRjztzr7cJVEXV2taMGUyCCemcTSpyZzvsnoCV
Pools
5 fixed sizes
0.1 SOL1 SOL10 SOL100 USDC1,000 USDC
Proof
~1.5 s
Groth16 over BN254, made on your own machine. About 116k compute units to check on chain.
Fees
0.3%
Protocol fee, all of it to buyback and burn. The gateway fee is 0.002 SOL or 0.6 USDC by default.
Network
2 hops
A relay sees your IP, a gateway sees the request. No single server sees both.

Add it to an agent

claude mcp add shroud -e SHROUD_WALLET=~/agent.json -- npx -y shroud-sol mcp

01

Overview

Shroud gives an agent, or a person, three things that work together.

Zero-knowledge proofs

Shielded pools break the link between the wallet that puts money in and the wallet that takes it out. The withdrawal carries a Groth16 proof, made on your own machine, that one of the deposits is yours without saying which. The program checks it on chain.

The proof

A private network

Relays and gateways break the link between your IP and what you ask for: a relay sees who, a gateway sees what, and no single server sees both.

Relays and gateways

An interface for AI agents

One MCP server with seven tools. Any agent that speaks MCP can shield, send privately, swap from a fresh wallet and read the chain through the network, with no account and no API key.

For AI agents

02

Pools and notes

Each pool takes one size only. Because every deposit in a pool is the same amount, one deposit cannot be told from another by its size.

  • 0.1SOL
  • 1SOL
  • 10SOL
  • 100USDC
  • 1,000USDC

When you shield, your device makes a note. Only its hash goes on chain, as the next leaf of the pool's Merkle tree (depth 20, so 1,048,576 deposits per pool).

Note
Two random numbers made on your device: the nullifier and the secret. The note is the money.
Commitment
A hash of the note, and the only part of it that goes on chain.
Nullifier hash
Shown when you withdraw, so the same note can never be spent twice.
A Merkle tree twenty levels deep with one root. A red line from your wallet enters the row of leaves at the bottom, labelled leaf = Poseidon(nullifier, secret). Beside it: 1,048,576 deposits per pool, and the five pools with their addresses.
Fig. 1Every deposit in a pool is the same size and only its hash enters the tree, so the deposit your wallet made in public becomes one of 1,048,576 identical leavesFull size

03

The proof

To withdraw, your device makes a Groth16 zero-knowledge proof (circom, Poseidon hashing) that says, without saying which deposit:

  1. I know the note behind one of the commitments in this pool's tree.

    root
  2. That commitment is also in the clean set the keeper published.

    assocRoot
  3. Here is its nullifier hash, so the same note can never be spent twice.

    nullifierHash
  4. The recipient, the gateway and its fee are exactly these.

    extDataHash
On your machine, the note (nullifier, secret) and its path in both trees go into a Groth16 prover. Across a dotted line, on Solana: a 256-byte proof and four public inputs (root, assocRoot, nullifierHash, extDataHash), verified on chain with the alt_bn128 syscalls in about 116k compute units.
Fig. 2Your note and the path to your deposit stay on your machine; the program gets four public inputs and a 256-byte proofFull size

The program verifies it on chain with Solana's alt_bn128 syscalls (about 116,000 compute units for a SOL withdrawal), records the nullifier, and pays out of the pool in the same transaction.

Making the proof takes about 1.5 seconds for an agent. A full private send from the browser took 3.8 seconds on a desktop and 4.0 seconds on a phone in our rehearsal.

Nobody can redirect it

The fourth input is a hash of the recipient, the gateway and its fee. Change any of them and the proof fails, so whoever carries it cannot redirect the money.

Two rows. In the first, recipient, gateway and fee are hashed into extDataHash and the proof verifies: valid, pays the recipient. In the second the gateway swaps in its own wallet, the hash is different, and the same proof fails: InvalidProof, nothing moves.
Fig. 3A gateway that changes the recipient, itself or its fee gets InvalidProof, and no money movesFull size

04

Relays and gateways

A withdrawal still has to reach the chain, and whoever sends it learns your IP. Shroud splits that job in two, following Oblivious HTTP (RFC 9458).

RoleSeesNever sees
RelayYour IP, a sealed blob, which gateway it goes toWhat is inside
GatewayThe request: a withdrawal, a balance check, a swap quoteWho sent it
You, a relay, a gateway and Solana on one line. Between you and the relay: your IP and a sealed blob. Between relay and gateway: the sealed blob and the relay's IP. Between gateway and Solana, in red: the request, in the clear. Below, who sees what, and the seal: DHKEM(X25519, HKDF-SHA256), AES-128-GCM, RFC 9458.
Fig. 4Requests are sealed to the gateway's key and passed to a relay run by another operator: the relay learns who, the gateway learns what, and neither learns bothFull size

Your device seals each request to a gateway's HPKE key (X25519, HKDF-SHA256, AES-128-GCM) and hands it to a relay run by a different operator. Both are registered on chain with their endpoint, key and bond, so the list comes from the program, not from us.

The gateway also acts as the relayer: it pays for the withdrawal transaction and takes the fee bound into the proof, so the receiving wallet needs no SOL and never signs.

What a gateway answers

A fixed list of calls.

  • Solana reads (getProgramAccounts only for the Shroud program)
  • Sending transactions
  • Jupiter quotes and swaps
  • The clean set
  • Withdrawals

05

Clean set and ragequit

A pool that takes anyone's money ends up holding stolen money, and then everyone in it looks guilty. Shroud uses an association set, as in Privacy Pools: a keeper reviews deposits and publishes the root of the ones it approves. A withdrawal must prove membership in that set as well as in the pool.

  • New deposits wait for a 30-minute review before they are approved.
  • Deposits from wallets flagged for hacks or theft are left out. If one is flagged after approval, the keeper resets the set and every older root stops working.
  • The keeper serves the approved list; your device checks it against the root on chain, so it cannot hand you a set it did not publish.
Two trees side by side, the pool and the clean set, joined by one withdrawal proof that shows membership in both. One flagged deposit is left out of the clean set by the keeper after a 30-minute review and goes back to its own wallet by ragequit, in public. Withdraw and ragequit are never paused.
Fig. 5A withdrawal proves its deposit is in the pool and in the keeper's clean set; a deposit left out can always go back to the wallet it came fromFull size

Ragequit

The wallet that made a deposit can always take it back, in full, to itself, in public. Nobody's money is stuck, and the private exit stays closed to stolen funds.

The admin can pause new deposits and node sign-ups. Withdrawals and ragequits have no pause in the program.

06

Fees

A private withdrawal pays two fees. Ragequit has no fee, and a deposit costs only the Solana transaction and a small deposit record.

Gateway fee
Set by each gateway and bound into your proof. Defaults: 0.002 SOL in SOL pools (covers the nullifier account and the transaction), 0.6 USDC in USDC pools (also covers opening the recipient's token account).
Protocol fee
0.3% of the pool size, paid in the same transaction to the buyback address. Every hour that address spends what it holds (keeping a little SOL for its own fees) on $SHROUD through Jupiter and burns all of it, starting when the token launches. The program caps the fee at 1%.

Buyback address. Every swap and every burn is a public transaction from it

8Pna6caENb2RF9Zqjqy9v8AkXk7PBAkYMC2FSTtiDTgR

What arrives

For each pool, with the default gateway fee.

PoolGateway feeProtocol fee, 0.3%Arrives at the new wallet
0.1 SOL0.002 SOL0.0003 SOL0.0977 SOL
1 SOL0.002 SOL0.003 SOL0.995 SOL
10 SOL0.002 SOL0.03 SOL9.968 SOL
100 USDC0.6 USDC0.3 USDC99.1 USDC
1,000 USDC0.6 USDC3 USDC996.4 USDC
One 1 SOL withdrawal as a bar: 0.995 SOL to the new wallet, and the last 0.005 SOL magnified into a 0.002 SOL gateway fee and a 0.003 SOL protocol fee in red. The protocol fee goes to the buyback address, which buys the token on Jupiter every hour and burns all of it. A table gives the amount per withdrawal for each pool.
Fig. 6Every withdrawal pays 0.3% of the pool size to one public address, which buys the token on Jupiter every hour and burns all of itFull size

07

For AI agents (MCP)

The Shroud CLI is the npm package shroud-sol and includes an MCP server. Nothing to install: point it at the agent's wallet keypair and add it to any MCP client.

Claude Codeshell
claude mcp add shroud -e SHROUD_WALLET=~/agent.json -- npx -y shroud-sol mcp
Any MCP client, over stdiojson
{
  "command": "npx",
  "args": ["-y", "shroud-sol", "mcp"],
  "env": {
    "SHROUD_WALLET": "/path/agent.json"
  }
}

The zero-knowledge proofs are made on the agent's machine, in about 1.5 seconds, with the proving key shipped in the package. There is no account and no API key.

Seven tools

shroud_pools
Pools, sizes, deposits, withdrawals.
shroud_shield
Deposit from the agent wallet; the note goes to ~/.shroud/notes.json (0600).
shroud_notes
Each stored note: in the pool, in the clean set, spent.
private_send
Withdraw a note to any address through relay → gateway.
private_swap
Withdraw a SOL note into a new wallet and buy a token from it via Jupiter, every call through the network. The new wallet’s key is stored with the note.
private_rpc
Any read-only Solana call through the network.
shroud_ragequit
Take a deposit back to the agent wallet, in public.
One call, private_swap, with a note id, an output mint and a slippage. Below it a line: a 1 SOL note from shroud_shield is withdrawn through relay and gateway into a new wallet that holds 0.995 SOL and has no past, then a Jupiter buy through relay and gateway leaves the token in the new wallet.
Fig. 7One call: private_swap moves a SOL note into a fresh wallet and buys through Jupiter, all through relay and gatewayFull size

08

Run a node

Anyone can run a relay, a gateway or both. Registration locks a bond: SOL until $SHROUD exists (0.5 SOL today), then $SHROUD staked into an account the node owns. Leaving starts a seven-day cooldown, after which the whole bond comes back.

Register on chainshell
shroud register --operator op.json --hpke-seed hpke.key \
  --endpoint https://node.example.com --roles relay,gateway
Run the nodeshell
shroud node --operator op.json --hpke-seed hpke.key --port 7401 \
  --upstream <your RPC> --asp <clean-set keeper URL>
A gateway
Earns the fee of every withdrawal it sends and pays its transaction costs from the operator wallet. It keeps no logs of requests.
A relay
Earns nothing directly today; it is the half of the network that keeps gateways from seeing IPs. Running both roles under one operator gives no extra privacy, so clients always pick a relay and a gateway from different operators.
The admin
Can switch a misbehaving node off. Its bond stays its own.

09

What you trust

The trusted setup
Groth16 needs a one-time ceremony. If every participant colluded, they could forge withdrawals. Mainnet keys come from a public ceremony on top of a public powers-of-tau; one honest participant is enough. Read the transcript
The program and whoever can upgrade it
Until the upgrade authority is given up, its holder can change the code. Check the authority on any explorer before you deposit size.
The keeper
Decides which deposits are in the clean set. It can delay or exclude your deposit, never take it: ragequit always works.
Two operators not colluding
A relay and a gateway that share their logs could link an IP to a request. Clients pick them from different operator keys. At launch the first relay and gateway are run by the team, so this protection becomes real as independent operators join; the node list on chain shows who runs what.

10

Trusted setup

The proving key on mainnet comes from a phase-2 ceremony on top of the ppot_0080_15.ptau powers of tau. Each contribution below added randomness that was thrown away, and a beacon from a Solana blockhash closed it; the key is safe if any one contributor really threw theirs away.

A line of five steps: the PSE powers of tau file ppot_0080_15 with 80 contributions, team contribution 1, team contribution 2, a beacon in red taken from the blockhash of Solana slot 467990330, and the verifying key in the program. Each step shows the start of its hash, and a time axis below shows when each happened on 2026-11-11 UTC.
Fig. 8On top of the 80-contribution PSE powers of tau: two team contributions, then a Solana blockhash as beacon, then the verifying key the program checksFull size

The transcript

  1. Powers of tauppot_0080_15.ptau
    SHA-2564a8eb5a754ed4710d6ad603c2e1a89c72325a448e4474f8694d2c71c7c272f3d

    Checked in full (every contribution) with snarkjs on 2026-11-11.

  2. Shroud team, contribution 1
    Contribution hashf556dba7 9eccee4b 9adfd7fd 11cf130a 401744ef 40b3a4d1 46c1aa5d af0785db e1aff5b2 11998d99 59a8c9b8 1e028b4d 8f925eb5 e291c044 76b4be77 8fa18bbb
  3. Shroud team, contribution 2
    Contribution hash17ac393d ece8e378 7ebc81f4 2a1f2290 739cbb21 00c4a02c b075d6d3 d9055bf3 4fc3acb3 8dba8d57 b5f5612f d9de191e bfe5d828 837a6d1e 60c8f37f 871c4f97
  4. Beacon
    Valuecd26d81cb4d18a46ec8fe6b703eed75d623976bd6cb8dd7c5be43024ec8aef7a

    The blockhash Eopw4NmiT2drJTUZnPuFwVFBcbPv37hMUKCWXvUFrMXw of Solana mainnet slot 467990330 (picked at 2026-11-11T04:32:04Z, when the finalized tip was slot 467990180). 10 iterations.

  5. Verifying key
    SHA-25645942b7602f11c1031eb7c5c2d6cf79afcc5199ce353aa02b18c5cff60d6f4b1

    The key the program was built with: /zk/verification_key.json

Check it yourself

This replays every step against the circuit and the public powers of tau.

In circuits/shell
node scripts/ceremony.mjs verify

11

What it does not hide

Shielded pools break the link between a deposit and a withdrawal. These stay visible, with Shroud or without.

That you deposited
From which wallet, and when.
Timing
Depositing and withdrawing a minute apart into a quiet pool narrows it down to a few people. Wait, and let deposits pile up between.
Habits
Withdrawing to a wallet you already use, or always the same amount right after you deposit, links them by behaviour.
Your IP, when you deposit
Not on chain, but your own wallet's RPC sees it when you deposit through it. Withdrawals and reads through the network do not reveal it.

12

Program

Program id
D2XCVLhRjztzr7cJVEXV2taMGUyCCemcTSpyZzvsnoCV
Deployed
Slot 467992659, 2026-11-11
Executable hash
c9112f851a3a3f28e57f858546f86d3f143771b18a236ea027c73c8e33b8f656
Verifier
Groth16 over BN254, 4 public inputs
Tree
Poseidon, depth 20, 30 recent roots
Clean-set roots kept
16

Instructions

  • deposit
  • withdraw
  • ragequit
  • set_assoc_root
  • reset_assoc_roots
  • register_node (SOL or staked)
  • update_node
  • leave_node
  • reclaim_node
  • admin config