Anonymous
Pipoke ships an anonymous-by-default option for posting, for voice rooms, and for auctions. All three use one primitive: a Groth16 BN254 zero-knowledge proof that you are a member of a Merkle commitment set, plus a per-action nullifier that prevents replay.
Pipoke runs on Octra Devnet today. Any fee, price, or limit referred to here is a contract setting chosen for testing. Every one is owner-settable, and mainnet values will be different. These docs describe how the mechanics work, not what the numbers are.
#The primitives
| Layer | What it is |
|---|---|
| Curve | BN254. The same elliptic curve Octra exposes as a native verifier via groth16_verify_bn254. |
| Hash | Poseidon over the BN254 scalar field. Cheap to compute in a SNARK circuit. |
| Identity | A local secret. Lives in your browser, derived from your wallet signature. |
| Commitment | Poseidon([secret]) cast to a field element under the BN254 scalar modulus. 64 hex chars on the wire. |
| Inclusion | A Merkle tree of every registered commitment. |
| Membership proof | A Groth16 BN254 SNARK proving your commitment is in the set, verified on-chain in a single epoch. |
| Replay protection | A nullifier derived from Poseidon([secret, intent_hash]). Used once per (intent, epoch). |
| Storage | Ciphertext lives in your Circle. The chain holds the proof, the nullifier, and the receipt. |
The BN254 scalar field modulus is 21888242871839275222246405745257275088548364400416034343698204186575808495617. Both your commitment and your nullifier are field elements under that modulus.
#Anon commitment registration
Before you can act anonymously, you register your commitment.
PipokeAnonCommitmentRegistry.register(commitment_hex)
commitment_hex is 64 hex chars (the Poseidon output cast to a field element). The Merkle tree of all registered commitments is rebuilt and a fresh root is published on PipokeAnonReputationRoots on a short cadence. Your commitment becomes provable within one publish cycle.
#Nullifier derivation
For each anonymous action, the app derives an intent hash that binds the action to a context (the shard, the epoch, the post being co-signed, the auction being bid on, and so on). Then:
nullifier = Poseidon([secret, intent_hash])
The intent hash is a SHA-256 of a canonical JSON binding, cast to a field element. Submitting the same nullifier twice in the same context fails: the contract stores nullifiers in a set per shard and rejects duplicates.
This is what gives Pipoke "one anon action per intent per epoch" without ever needing to know which member of the anon set did it.
#Submission phases
The app surfaces the submit lifecycle as four phases.
| Phase | What happens |
|---|---|
preparing |
Load the current root, hash the intent, set up circuit inputs. |
proving |
Run the snarkjs WASM circuit in the browser. This is the slow step. |
submitting |
Send proof, public_signals, nullifier, root_epoch to the shard. |
done |
Indexer confirms the receipt. |
A typical anon post produces a ~256KB snarkjs proof. The contract calls groth16_verify_bn254(vk, proof, public_signals) and accepts the post if the proof verifies, the root in the public signals is a recently published one, and the nullifier is fresh.
#Where the anon surface shows up
The same primitive powers four user-facing features.
| Surface | Where the proof lands | Identity model |
|---|---|---|
| Anon post | One of 8 anon feed shards | Anon set membership. Nullifier scoped to (anon-post, epoch). |
| Anon co-sign | Same anon feed shards | Same membership proof, nullifier scoped to (anon-cosign, target_post). |
| Backroom open | PipokeRooms.open_backroom |
Membership proof, nullifier scoped to (backroom-open, epoch). |
| Anon auction bid | One of 16 anon auction shards | Separate anon-bidder credential. Per-auction anon_bid_nonce for replay. |
#The 8-shard anon feed
The anon feed runs across 8 shards. A new anon post or anon co-sign lands on one shard, determined by routing math on the anon-set epoch. The indexer aggregates the 8 shards into one unified anon feed.
#What anonymity does not cover
Anon does not hide:
- That a post happened. The on-chain record exists.
- The time the post happened. The epoch is on chain.
- The shard the post landed on. The shard ID is part of the post ID.
- Metadata you put into the post yourself.
Anonymity covers the link from the post back to your wallet. That is the strong guarantee zero-knowledge proofs are designed to provide.
#Who anon is for
| Group | What anon does for them |
|---|---|
| Whistleblowers | Publish without naming yourself. The platform cannot subpoena what it does not have. |
| Dissidents and journalists | Co-sign sensitive posts anonymously. Speak in a backroom with a masked identity. Bid on a sealed auction with a separate anon credential. |
| Most users | A switch you can flip for a single thread, a single room, a single bid, without leaving Pipoke. |