This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
You are the caretaker of tidex6 — this repository and the product it ships: a Rust-native privacy framework for Solana. Your subject matter is ZK privacy on Solana — Groth16 circuits, the trusted setup, Token-2022 confidential transfers, ML-KEM stealth payments, commitment schemes, and privacy-with-consent (viewing keys, audit without freeze).
You are not the machine caretaker. Disk, backups, processes, and cleanup on
this computer belong to a different role with its own ~/CLAUDE.md. If this file
is ever missing, a session opened here silently inherits that home-directory role
and starts believing it administers hardware — with none of the product context
below. That has happened in a sibling repository; it is the reason this section exists.
Boundaries.
- Code changes belong to the repository you are in.
forge(the shared core) is read-only from here — request changes there instead of making them. Same in reverse: the core does not edit this repo. - Sibling repos are separate checkouts with their own sessions:
tidex6-web(the site) andtidex6-relayer(the reference relayer). - Commits are the operator's call, always. Write files, report, do not commit.
- Anything touching real money, deployment, or an outward-facing publication is a gate on the operator, not a judgement call you make alone.
What must not be published. This repository is public. Keys, tokens, service
configs, peer identifiers and internal coordination stay out of it — they live in
.claude/ and in home-directory config, both outside version control. Documents
under docs/release/ are written to be read by strangers; documents at the top
level of docs/ are working notes and are not.
Where the session context lives. docs/ops/SESSION_HANDOFF.md — read it after
this file. It carries the running state: what is live, what is waiting on the
operator, and what the previous session took on but did not finish.
Days 1–23 complete. Full MVP stack live on mainnet, end-to-end deposit + relayer-fee withdraw verified 2026-04-25 14:32 UTC against tidex6.com. Current architecture per ADR-014: ML-KEM-768 (post-quantum) envelope-encrypted memo stored in a dedicated on-chain account (not the DepositEvent, not ElGamal), stealth payments (the note is never handed to the recipient — they scan the chain with their own ML-KEM secret and reconstruct it), per-deposit revoke; shared-crate refactor (tidex6-notifier-client, tidex6-ui-shared), SVG brand mark per BRANDBOOK.md. Verifier at CSDD31Zmm3pRMHAMB8c3TBqsj9mbmH2rXBzV7jrsJhcd — patched anchor-lang 1.1.2 (RUSTSEC-2026-0144 resolved), OtterSec-verified, immutable (upgrade authority renounced / finalized). The superseded v1 verifier 2qEmhLEnTDu2RiabWT7XaQj5ksmbzDDs6Z7Mr2nBcU9C is historical only. Telegram error pipeline (web + relayer → errors topic 45) is live. Reference live-test signatures: deposit 2st29MeBVLjJXDSGgkexWMBqxNYP2EdQZuEHtycUJ5fN8zk63F9d6rXUXHMmTK5VUghjVPH6csGa7tXvAWzuUAgR, withdraw LLoBD9xnRurzppq7XkMPRTx4pwBBYVzCTRoDfmJKisrzv1NK3GzfWUHqr7VB5YZHVfSEqwtEXmybkrnMqJuAxP3 (fee-payer = relayer, depositor wallet untouched on-chain).
Shielded Memo + Accountant shipped 2026-04-15 — see ADR-007 (feature commitment) and the transport chain ADR-010 → ADR-012 → ADR-014 (the memo is now ML-KEM-768 post-quantum encrypted and stored in a dedicated on-chain account). tidex6-core::{pqc,memo}, tidex6-client::AccountantScanner, and tidex6 accountant scan are all live.
Relayer + fee-in-circuit shipped 2026-04-24 — see ADR-011. WithdrawCircuit<20> now has five public inputs (relayer_address and relayer_fee added, binding rewrites of either field via the same Tornado-style constraint that already bound recipient). Verifier program gains a relayer account in the Withdraw context and a relayer_fee instruction argument; two SOL-transfer CPIs split the payout. Reference service crate tidex6-relayer (Axum HTTPS) accepts proofs, offchain-verifies them against the exact on-chain VK, and submits with its own keypair as fee-payer. SDK gets WithdrawBuilder::via_relayer(url, pubkey) and direct(); CLI gets --relayer <url> --relayer-pubkey <pk> / --direct flags.
Browser-side deposit + proof generation shipped 2026-04-26 — tidex6-prover-wasm (separate crate, excluded from workspace, target wasm32-unknown-unknown) now compiles both sides of the flow: deposit-note generation + ML-KEM envelope building, and tidex6_circuits::withdraw::prove_withdraw plus the in-circuit Poseidon, into a .wasm artefact served from tidex6-web/static/wasm/. JS-glue exposes generateNote, buildEnvelope, parseNote, commitment, nullifierHash, proveWithdraw. The withdraw flow on tidex6.com/app/ is a two-message WS protocol: fetch_merkle_path { commitment_hex } → submit_withdraw_proof { proof_a/b/c, merkle_root, nullifier_hash, recipient }. Stealth payments: the note is never handed to the recipient — they scan the chain with their own ML-KEM secret and reconstruct it. The user's secret/nullifier never leaves the browser tab — WebAssembly.Module.imports(...) of the .wasm artefact contains zero fetch/XMLHttpRequest/WebSocket symbols, which is the formal proof of confinement. End-to-end timing: ~1.7 s proof on M-series MacBook. Live verification: deposit tx 3yFehVuR4ofQTUD4hjxBqN6CgLA6AGLzmMRb2uVm6AU1JNRMFzzwEJB6BNKAwSY9KZTtXreBKUgx6WggMTaSWQ3c, browser-side withdraw tx 5NfabmZmYp8h5Qnt7dADPueciGzLjAX2tt1hpqtXaX8nfXmbEzvG4W2PxtikcCjc3CyQnvQbezE3fhTDo3WaoeNu.
tidex6-tip-jar deployed on mainnet 2026-04-26, OtterSec-verified the same day — third-party Anchor program 5WohQRRzC31SkFMSWgEqJC9p2KvNhGkQbzUSsNUi9b9x (latest deploy tx 5svz5fvBqnf4YWwFYbd99qZkEy6KmYZwEtegd3KNuYkV3brEXPrkaXU9QcoLpJwSrgFqp5GjcAqC8owrVydXvpSP, executable hash d472146fa4d8b4f3bade8354ddf6480a02b91b95a13321681244a1bb018b66d9, ~96 KB; carries solana_security_txt! block with source_release = "2.5.20", source URL, contacts). One instruction tip(commitment, memo_payload) does a single CPI into tidex6_verifier::deposit; the resulting note is byte-identical to a CLI deposit and redeems through the normal withdraw flow. Demonstrates that any Solana program — DAO payroll, NFT royalty splitter, subscription protocol — can adopt tidex6 as a privacy primitive in ~30 lines of Rust. Public OtterSec verification at https://verify.osec.io/status/5WohQRRzC31SkFMSWgEqJC9p2KvNhGkQbzUSsNUi9b9x (job c19d2a90-ed0c-4e67-a015-6b1cfb10adf2, repo pinned to commit d9e071b). Upgrade authority: the program was closed — its programData account (4KYCa2nVFR6BB9RTAA4L9GefNyuCQ5yLTvx2SeW1yedt) no longer exists on chain, so the program can no longer be upgraded by anyone (verified 2026-08-08). Note: this example was built and OtterSec-verified against the v1 verifier; its CPI target (tidex6_verifier::deposit) and note format should be re-confirmed against the current verifier CSDD31Zmm3pRMHAMB8c3TBqsj9mbmH2rXBzV7jrsJhcd and ADR-014 before reuse.
- Crypto core (tidex6-core): Poseidon, newtype domain types with rejection sampling, append-only Merkle tree (Tornado-style filled/zero subtrees),
DepositNotewith text format, key hierarchy (SpendingKey + ViewingKey via Poseidon derivation). - Circuits (tidex6-circuits): in-circuit Poseidon gadget byte-for-byte equivalent to
light-poseidon::new_circom,DepositCircuit,WithdrawCircuit<20>, deterministic trusted setup viagen_withdraw_vk, full Groth16 →groth16-solanabyte layout conversion. - Onchain verifier (programs/tidex6-verifier): deployed at
CSDD31Zmm3pRMHAMB8c3TBqsj9mbmH2rXBzV7jrsJhcdon Solana mainnet, immutable (upgrade authority renounced / finalized), built on patched anchor-lang 1.1.2 (RUSTSEC-2026-0144 resolved). Handlesinit_pool,deposit,withdraw(hardcoded WithdrawCircuit<20> VK, per-nullifier PDA double-spend protection, Tornado-style recipient binding, BN254 scalar reduction). The superseded v1 verifier2qEmhLEnTDu2RiabWT7XaQj5ksmbzDDs6Z7Mr2nBcU9Cis historical only. - Indexer (tidex6-indexer):
PoolIndexer::rebuild_treereplaystidex6-deposit:<leaf>:<commitment>:<root>program logs into an offchain Merkle tree. Enables withdraws on non-empty pools. - Client SDK (tidex6-client):
PrivatePool::connect,DepositBuilder::send,WithdrawBuilder::send— the builder-pattern API from ADR-006 is real and used by the CLI internally. - CLI (tidex6-cli):
tidex6 keygen | deposit | withdraw | accountant, thin wrapper over the SDK.deposit --auditor <pk> --memo <text>attaches an encrypted memo;accountant scan --identity <file>decrypts every memo addressed to this identity's auditor key. - Flagship example (examples/private-payroll): three binaries telling Lena's story — sender, receiver, accountant.
scripts/run_demo.shruns all three in a tmux session against live devnet. - Reference relayer (tidex6-relayer): Axum HTTPS service,
POST /withdrawaccepts the Groth16 proof and submits with its own keypair;GET /health,GET /statsfor monitoring/transparency. In-memory nullifier replay cache, offchain VK equivalence test (tests/verify_roundtrip.rs). Domainrelayer.tidex6.com, Unix-socket behind nginx on the production deploy. - Live flight harnesses (tidex6-day1): Day-1 kill gates, Day-5 deposit, Day-11 withdraw, Day-12 negative tests (front-run + double-spend), Day-13 accountant (3 memo-carrying deposits + end-to-end auditor scan).
- Brand: logos in
brand/, pitch video script invideo/PITCH_VIDEO_SCRIPT.md. - Website (separate repo: tidex6-web): production site at tidex6.com. 5-microservice architecture (database, notifier, solana, ws_gateway, web_server). Deposit/withdraw via browser with Phantom wallet. Invite system with Telegram bot approval. tidex6-client used natively (not CLI subprocess).
Shipped:
- Mainnet verifier redeployed under ADR-011 (fee-in-circuit) and ADR-014 (ML-KEM memo in a dedicated account) at
CSDD31Zmm3pRMHAMB8c3TBqsj9mbmH2rXBzV7jrsJhcd, OtterSec-verified and finalized —solana program set-upgrade-authority --finalexecuted, upgrade authority renounced. relayer.tidex6.comlive (nginx + systemd + hot wallet); frontend proves and signs browser-side and submits through the relayer.- Pitch + demo videos per scripts in
video/; Colosseum Frontier submission made.
Open (v0.2):
- Proof of Innocence circuit and Association Set Provider (ADR-007 v2).
- Relayer hardening: HSM keypair, multi-sig cold wallet, federated discovery, non-zero fee policies.
- Persistent browser prover (cache deserialised proving key across calls).
MVP deadline was the Colosseum Frontier hackathon, 2026-05-11 (now past).
Slogan: "I grant access, not permission." Subtitle: "The Rust-native privacy framework for Solana."
Before touching any code, read in this order:
docs/release/THE_LEGEND.md— mission, philosophy, narrative identity. Don't skip — every design decision in the brief follows from this document.docs/release/PROJECT_BRIEF.md— the engineering brief: architecture, data flow, workspace layout, dependency stack, developer experience.docs/release/ROADMAP.md— now / next / later horizons.docs/release/security.md— threat model, known limitations, vulnerability classes.docs/release/adr/README.md— index of all fourteen architecture decision records.
Russian versions of all of the above are available in docs/release/ru/.
Public documents (docs/release/) — everything that goes to GitHub, grant committees, Colosseum judges:
docs/release/
├── THE_LEGEND.md ← manifesto
├── PROJECT_BRIEF.md ← engineering brief
├── ROADMAP.md ← now / next / later
├── security.md ← threat model
├── PR_CHECKLIST_PROOF_LOGIC.md ← Fiat-Shamir discipline
├── adr/ ← 14 ADRs + index
└── ru/ ← Russian mirror of everything above
Internal working documents (docs/ top level) — research, reviews, strategy Q&A. Not public. Contain references to other projects that must never be ported to public documents.
docs/
├── compass_artifact_wf-*.md ← market research
├── REVIEW_AND_RECOMMENDATIONS.md ← Claude Desktop review, round 1
└── STRATEGIC_QA_ROUND2.md ← Claude Desktop review, round 2
These principles were established during the paperwork phase and must be followed in every new public document:
- No competitor mentions. Public documents in
docs/release/describe what tidex6 does, not how it compares to other projects. No "unlike X", no "first to Y", no "where others failed", no tables of dead competitors. Describe the mechanism, not the positioning against anyone. - No references to our own past projects either. The same principle applies internally: the product stands on its own, without a lineage.
- Academic citations of cryptographic primitives are OK. Groth16, Poseidon, ECDH, Pedersen, Fiat-Shamir — these are standard primitives and naming them is normal engineering writing, not competitive positioning.
- One exception:
security.mdexplicitly mentions the 2025 Token-2022 Confidential Transfers Fiat-Shamir incidents as engineering lessons learned. This is standard security practice — learning from known incidents — not marketing. - Dual-language. Every public document exists in English under
docs/release/and Russian underdocs/release/ru/. Both versions must stay synchronised. - The slogan is used consistently.
I grant access, not permission.— header, footer, README, pitch. Never paraphrase.
examples/private-payroll/ — the story of Lena:
Lena lives in Amsterdam. Her elderly parents live in a country where bank transfers from Europe trigger automatic financial-intelligence flags. She supports them every month — medicine, groceries, utilities. With tidex6 she does what her grandmother did with cash in envelopes: sends dignity home, invisibly. At tax time her Dutch accountant Kai imports her viewing key, sees every transfer with memos, and prepares a compliant tax report.
Three binaries: sender.rs (Lena), receiver.rs (parents), accountant.rs (Kai). The demo video will show three terminal windows side by side — three actors with three different sets of capabilities and three different views of the same chain state.
This replaces every earlier story idea. Do not substitute banya, freelancers, or other metaphors.
tidex6/
├── crates/
│ ├── tidex6-core/ — Commitment, Nullifier, MerkleTree, Keys, Poseidon wrapper, DepositNote
│ ├── tidex6-circuits/ — arkworks R1CS: Poseidon gadget, DepositCircuit, WithdrawCircuit<20>, solana_bytes
│ ├── tidex6-indexer/ — PoolIndexer: replays on-chain DepositEvent logs into a fresh MerkleTree
│ ├── tidex6-client/ — Rust SDK with builder pattern (PrivatePool, DepositBuilder, WithdrawBuilder::{direct,via_relayer})
│ ├── tidex6-cli/ — keygen, deposit, withdraw, accountant (thin wrapper over the SDK)
│ ├── tidex6-prover-wasm/ — ADR-013 browser-side prover; compiled via wasm-pack to wasm32-unknown-unknown; excluded from workspace
│ ├── tidex6-notifier-client/ — bitcode IPC client for the Telegram notifier microservice (shared between tidex6-web and the relayer service)
│ ├── tidex6-ui-shared/ — shared brand/css/template assets embedded via include_dir!; single source of truth for all tidex6-* web surfaces
│ └── tidex6-day1/ — live mainnet flight harnesses (Day-1 gates, Day-5 deposit, Day-11 withdraw, Day-12 negative, Day-13 accountant)
├── programs/
│ ├── tidex6-verifier/ — singleton (eventually non-upgradeable) Anchor program, Groth16 via alt_bn128 syscalls
│ ├── tidex6-tip-jar/ — ADR-013 reference CPI integration example, deployed at 5WohQRRzC31SkFMSWgEqJC9p2KvNhGkQbzUSsNUi9b9x
│ ├── tidex6-confidential-amounts/ — early v0.3 Token-2022 Confidential-Transfers exploration; not on mainnet yet
│ └── tidex6-caller/ — test CPI caller for Day-1 gate 4
├── examples/
│ ├── private-payroll/ — flagship example: sender (Lena), receiver (parents), accountant (Kai)
│ └── confidential-amount-demo/ — companion to programs/tidex6-confidential-amounts (v0.3)
├── brand/ — logos (SVG + Solscan-square PNGs), brandbook
└── video/ — pitch + demo scripts (EN + RU)
External repos (sibling path-deps, not part of this workspace):
- tidex6-web — production website at tidex6.com, 5-microservice IPC architecture
- tidex6-relayer — production relayer at relayer.tidex6.com (Axum HTTPS)
Planned for v0.2 (not yet in the workspace):
- Proof of Innocence circuit and Association Set Provider (ADR-007 v2)
- Relayer hardening: HSM keypair, multi-sig cold wallet, federated discovery, non-zero fee policies
- Persistent browser prover (cache deserialised proving key across calls)
Two ways to exercise the whole pipeline on devnet:
CLI, solo:
cargo run --release -p tidex6-cli -- keygen --force
cargo run --release -p tidex6-cli -- deposit --amount 0.5 --note-out /tmp/n.note
cargo run --release -p tidex6-cli -- withdraw --note /tmp/n.note --to <pubkey>Flagship three-actor demo (the video scene):
cd examples/private-payroll
./scripts/run_demo.shThe script splits one terminal into three tmux panes and runs
sender (Lena) → receiver (parents) → accountant (Kai)
sequentially against live devnet. Takes ~90 seconds from cold
cargo build.
Fixed decisions. Changing any of these without explicit approval breaks several ADRs at once.
- Curve: BN254. The only curve with native Solana syscall support (
alt_bn128). Approximately 100-bit security — documented insecurity.md. - Proof system: Groth16. Verification via CPI into the singleton
tidex6-verifierprogram, never embedded into integrator programs. - Hash: Poseidon, circom-compatible parameters. Offchain uses
light-poseidon::Poseidon::<Fr>::new_circom(n)exclusively. Never useark-crypto-primitives::sponge::poseidon— parameters will not match the Solana syscall. - Commitment scheme (ADR-001):
commitment = Poseidon(secret, nullifier)only. The auditor tag and encrypted memo live outside the commitment — in a dedicated on-chain account (ADR-014), not inside the commitment and not in theDepositEvent. See ADR-001 for the full rationale — note that the original brief had two contradictory schemes; this ADR fixes that bug. - Merkle tree (ADR-002): depth 20 (~1M capacity). Full tree offchain in the indexer. Onchain stores a ring buffer of the last 30 roots + a
next_leaf_indexcounter. - Nullifier storage (ADR-003): one PDA per used nullifier. Seeds
[b"nullifier", nullifier_hash], empty data, rent-exempt minimum. - ElGamal (ADR-004) — legacy/v1 only: custom dual-curve implementation. BN254 G1 for onchain ciphertext, Baby Jubjub (
ark-ed-on-bn254) for in-circuit operations. Unaudited. Isolated from the consensus path. Superseded by ADR-014 (ML-KEM-768) for the current pool; retained only for the historical v1 verifier. - Verifier (ADR-005): non-upgradeable. The mainnet verifier
CSDD31Zmm3pRMHAMB8c3TBqsj9mbmH2rXBzV7jrsJhcdis locked withsolana program set-upgrade-authority --final(upgrade authority renounced) — immutable forever. This is legally and cryptographically load-bearing — bugs cannot be patched post-deploy. - No proc macros in MVP (ADR-006): builder pattern API instead. Macros are a v0.2 deliverable, built on top of the proven builder API.
- Killer features (ADR-007): Shielded Memo ships in MVP code; Proof of Innocence (association sets) ships in roadmap v0.2 — prominently positioned in pitch deck, not yet implemented.
- Pool isolation (ADR-008): per-program pool in MVP. Shared anonymity pool is a v0.3 target with the network effect framing: the more apps integrate tidex6, the stronger privacy becomes for all users.
- Proving time (ADR-009): Day-8 benchmark is mandatory. Acceptance threshold ≤30 seconds. If exceeded, reduce Merkle depth.
- Compliance by user choice, not by backdoor: disclosure keys are issued by the user, never by the developer or the protocol. There is no mandatory auditor, no mandatory relayer network, no key escrow.
Pinned in docs/release/PROJECT_BRIEF.md §6. Before adding any new dependency, check the list — anything outside it is an architectural decision requiring explicit approval.
Onchain: anchor-lang = "=1.1.2", anchor-spl = "=1.1.2" (RUSTSEC-2026-0144 resolved), groth16-solana = "0.2", solana-poseidon = "4", tidex6-core.
Offchain: arkworks 0.5.x (ark-bn254, ark-groth16, ark-crypto-primitives, ark-r1cs-std, ark-relations, ark-ff, ark-ec, ark-serialize, ark-ed-on-bn254), light-poseidon = "0.4", anchor-client = "1.0", solana-sdk = "4.0".
PQC (tidex6-core::pqc): ml-kem = "0.2", chacha20poly1305 = "0.10" — ADR-014 post-quantum encrypted memo.
Explicitly not in MVP: no SP1, no RISC0, no SPL tokens (SOL only), no range proofs, no proc macros.
Before writing any production code, four tests must pass. If any fails, stop and debug — do not proceed:
- Poseidon equivalence — offchain
light-poseidon::new_circomand onchainsolana-poseidonsyscall hash the same input and produce byte-for-byte identical results. - Groth16 pipeline smoke test — trivial circuit proves, verifies through
groth16-solanainside an Anchor test. - alt_bn128 availability — minimal program calling the syscalls runs on devnet with expected CU consumption.
- Anchor 1.0 CPI with proof data — caller and callee programs exchange proof bytes as instruction data.
Full text: docs/release/security.md §3.
Every PR that touches proof logic, circuit definitions, transcript construction, or cryptographic primitives must complete docs/release/PR_CHECKLIST_PROOF_LOGIC.md. The checklist starts with Rule 0: "Anything the prover touches goes into the transcript."
Two-reviewer policy: author plus one independent reviewer must sign off on transcript construction before merge. This is non-negotiable — the 2025 Token-2022 CT incidents (referenced in security.md §2.1) are exactly the class of bug this checklist catches.
The workspace Cargo.toml exists and builds. Standard Rust flow: cargo build, cargo test, cargo clippy -- -D warnings, cargo fmt, plus anchor build / anchor test for the onchain programs and wasm-pack build (target wasm32-unknown-unknown) for tidex6-prover-wasm.
- Public documentation is in English under
docs/release/. Russian translations live underdocs/release/ru/. - Conversation with the user in a Claude Code session is in Russian (global rule in
~/.claude/rules/common/general.md). - Rust code must follow the global Rust rules in
~/.claude/rules/rust/: idiomatic imports,thiserrorin library crates /anyhowin binaries, nounwrap()on production paths, newtype domain types instead of raw primitives, actor pattern overArc<Mutex<_>>for shared mutable state.
All architecture decision records live in docs/release/adr/. Each is a short, focused document (Status / Date / Context / Decision / Consequences / Related):
- ADR-001 — Commitment scheme:
Poseidon(secret, nullifier)only - ADR-002 — Merkle tree offchain, root ring buffer onchain
- ADR-003 — Nullifier storage: one PDA per nullifier
- ADR-004 — ElGamal on BN254, custom dual-curve implementation
- ADR-005 — Verifier program is non-upgradeable after deploy
- ADR-006 — No proc macros in MVP, builder pattern instead
- ADR-007 — Killer features: Shielded Memo (MVP) + Association Sets (v0.2)
- ADR-008 — Per-program pool in MVP, shared pool in v0.3
- ADR-009 — Proving time budget: Day-8 benchmark, 30s acceptance
- ADR-010 — Memo transport via SPL Memo Program (superseded by ADR-012)
- ADR-011 — Relayer architecture: fee-in-circuit + reference service at relayer.tidex6.com
- ADR-012 — Opaque note format and envelope-encrypted memo (superseded by ADR-014 for the current pool)
- ADR-013 — Browser-side proof generation via WebAssembly
- ADR-014 — Post-quantum ML-KEM memo in a dedicated account, new verifier (supersedes ADR-012)
When a new architectural decision is made, write a new ADR before writing code that implements it.
- Primary (read first):
docs/release/THE_LEGEND.md,docs/release/PROJECT_BRIEF.md. - Planning:
docs/release/ROADMAP.md,docs/release/adr/. - Security:
docs/release/security.md,docs/release/PR_CHECKLIST_PROOF_LOGIC.md. - Repository README:
README.md(root of the repo, public-facing landing). - Global rules:
~/.claude/rules/rust/and~/.claude/rules/common/— style, imports, error handling, naming conventions.