Welcome to Cloakra
Cloakra is a shielded capital-allocation desk for organizations on Starknet mainnet. It settles grants, bug bounties, and contributor payouts through the STRK20 privacy pool, so who receives and how much stays private, while the pool's public legs stay public and verifiable.
On a transparent chain every organizational payout is also a disclosure. Paying a security researcher links their wallet to the vulnerability they reported. Splitting a grant among contributors publishes the salary table. Funding a round broadcasts the whole allocation strategy. Cloakra removes the recipient side of that disclosure without pretending the public side disappears.
Where everything lives
- Pool contract:
0x040337b1af3c663e…on Starknet mainnet - Source, licence, and submission manifest: GitHub
- The app itself: /app · receipt checker: /verify
The stack, in one paragraph
A static Next.js frontend talking to the canonical STRK20 pool through WalletAccountV6 in starknet.js. There are no Cloakra contracts, no backend, and no server-held keys: the pool's own batch call settles many transfers atomically, and every operation is signed by your own wallet. Cloakra never touches a viewing key - that is a protocol rule, not a policy choice.
How it works
Three stages. Only the middle one is private, and the documentation is explicit about that throughout.
- 1. Shield. You deposit STRK into the pool. This leg is public: the depositing address and the amount are visible onchain, forever.
- 2. Allocate privately. Splits, bounties and grants settle inside the pool as encrypted notes. Recipients, amounts, and the link back to your organization are unreadable.
- 3. Unshield. Recipients withdraw to a public address on their own schedule. That leg is public too. What is broken is the link between your deposit and their withdrawal.
Set up a wallet
STRK20 operations require a privacy-enabled wallet. Today that means Ready. Cloakra detects any Starknet wallet that registers through the browser (Argent X, Braavos, OKX and others will appear in the picker), but only wallets exposing wallet-API 0.10 or later can perform private operations. Capability is detected with a version query - the app never reads your balances to feature-detect.
NOT_REGISTERED until you run “Enable private tokens” inside Ready. That one-time flow itself deposits 6 STRK, which the pool fee consumes entirely - so budget for it before your first shield.StealthSplit
Pay a whole team from one shielded balance in a single atomic transaction: all transfers land or none do. Add recipients by hand or paste a CSV payroll - one address, amount per line, with comma, semicolon or tab separators. Every pasted row passes exactly the same validation as a typed one, and a malformed row is reported with its line number rather than silently skipped.
Inside the pool no recipient can read another recipient's allocation. Advise recipients not to unshield their exact row amount immediately, though: matching withdrawals let an outside observer partition the split.
GhostBounty
A single private payout to a security researcher. The payout transaction names no recipient, no amount, and nothing linking them to your programme. The module includes an onboarding checklist for the researcher, because they must have a privacy-enabled wallet with private tokens enabled before a payout can reach them.
StealthGrant
A whole grant round disbursed in one atomic transaction. Each grantee sees only their own award; the recipient list and per-grant amounts never appear onchain. Only your total deposit into the pool is public.
Treasury
Shield STRK into the pool and unshield back out, read your shielded balance (consent-gated - your wallet asks first), and review your public footprint: every pool leg the chain can see about your account, reconstructed from the pool's Deposit and Withdrawal events. The instructive part is what is absent from that list - the private transfers and splits between the legs.
What's private, what isn't
| Private (inside the pool) | Public (onchain) |
|---|---|
| Who receives a split, bounty, or grant | Your deposits into the pool (address + amount) |
| Per-recipient amounts | Any withdrawal to a public wallet (address + amount) |
| The link between payer and payee | That an address interacted with the pool, and when |
One consequence deserves emphasis: because both legs are public, timing and amount correlation can re-link them. Unshielding an amount that echoes a recent deposit, or moving funds minutes after shielding, undoes much of what the pool provides. Patience is part of the privacy.
Privacy warnings
Before any private operation opens your wallet, Cloakra checks your own public footprint for the mechanical mistakes and warns you:
- · A deposit within roughly the last hour - the operation is cheaply timing-correlatable.
- · An amount that approximately matches one of your public deposits.
- · An amount matching a deposit minus one or more pool fees - the net-of-fee tell.
- · An amount matching the sum of two public deposits.
Matching is deliberately approximate, within about one per cent. Real observers match approximately, so nudging an amount by dust does not help and the warning will not pretend otherwise - waiting does. Warnings never block a payment; they turn an accident into a choice.
Safe to unshield?
The payer check above looks at your own deposits. A recipient unshielding a payout faces the mirror-image risk: the chain shows some organization's deposit going in and, later, your withdrawal coming out - and if the two amounts rhyme, an observer pairs them without needing anything private. So before an unshield opens the wallet, Cloakra also reads the pool's recent public activity by every account and checks:
- · Whether the amount approximately echoes another account's recent public deposit, or that deposit net of one pool fee. Deposits the fee consumes entirely (a registration) are ignored.
- · Whether it looks like an equal share of such a deposit - 1/2 through 1/8, net of at most one fee, matched tightly because a split divides a deposit exactly. Recipients of a split who each unshield their exact row re-link the run from the other end.
- · Whether the receiving address has taken about this amount out of the pool before. Repeated equal withdrawals form a payroll cadence.
- · How busy the pool is. If few STRK withdrawals happened in the window (up to ~19 hours), yours has a thin crowd to hide in, and the check says so with the number.
The Unshield panel and the dashboard both show the pool's current STRK crowd - withdrawals and deposits by anyone over the window - so the figure is visible before you type an amount, not only when it triggers a warning. Other tokens the pool carries are ignored: an ETH deposit is not a crowd for a STRK withdrawal. Recency drives severity: an echo of a deposit from the last hour is high; from earlier in the day, medium.
Signed payout receipts
A shielded payout is private, which makes it awkward to prove. After a run settles, Cloakra can turn it into receipts with a single off-chain wallet signature (SNIP-12 typed data - free, no gas). The signature commits to the operation, the settlement transaction hash, the pool, the recipient count, and a salted Merkle root over every (recipient, amount) pair.
Each recipient receives a file carrying only their leaf and proof, so a receipt discloses nothing about anyone else in the run. The salts stop anyone brute-forcing the root against guessed address and amount pairs.
Verifying a receipt
Anyone can check a receipt at /verify - no wallet, no account, no trust in Cloakra. Four checks run:
- · The file is well-formed, and its chain and pool match the ones verification uses.
- · The recipient and amount really sit inside the signed commitment (Merkle proof).
- · The organization's account signed the run - checked onchain via SNIP-6
is_valid_signature. - · The referenced transaction settled and emitted STRK20 pool events.
The displayed amount is derived from the signed value rather than the file's human-readable field, so a receipt cannot be edited to show a different number under a passing check.
Auditing a run
A recipient holds one receipt. An auditor holds a folder of them and one question: did this organization pay what it claims? Drop every receipt from a run onto /verify - files, a JSON list, or one receipt per line - and the page becomes the auditor's desk:
- · Every receipt is verified with the same four checks, and grouped by the run its signed fields describe.
- · The org's signature and the settlement are checked once per run, however many receipts share them.
- · Each run reports coverage - receipts present against the recipient count the org signed - and the verified total, summed only over rows that pass.
- · Anomalies an honest run cannot produce are called out: two receipts naming one recipient, more recipients than the signed count, two attestations for the same settlement transaction.
- · The result downloads as a CSV, one line per receipt, with exact amounts.
Verdicts are deliberately narrow. Verified means every supplied receipt passes and the run is fully covered. Partial coverage means every supplied receipt passes but some are missing - the total covers only those present, and a missing receipt is a gap in the audit, not evidence of anything. Inconclusive means the chain could not be reached for a check; nothing is presumed. Does not verify means at least one row failed a check the receipt scheme can actually make.
Fees & registration
The pool charges a flat fee per private operation. Cloakra reads it live from the contract with get_fee_amount and never hardcodes it - the fee moved from 4 to 6 STRK during the sprint, which is precisely why. On a shield the fee is deducted from the deposit: shielding 26 STRK leaves a 20 STRK shielded balance.
The fee is re-read at signing time, and a submit is aborted if it changed between quote and signature rather than letting the transaction revert after you have already approved it.
Mainnet transactions
Cloakra's submission records seven mainnet transactions, all accepted on L1 with successful execution and confirmed to have emitted STRK20 pool events. The landing page re-verifies them against Starknet in your own browser on every visit - a fabricated hash renders a red NOT FOUND, so the claim is falsifiable through the interface itself.
The repository ships scripts/verify-tx.mjs and scripts/preflight.mjs if you would rather check them from a terminal than trust a web page.
Architecture
- No custom contracts.
strk20InvokeTransactionaccepts an array of actions settled atomically, so a multi-recipient split needs no bespoke Cairo. There is nothing of ours to audit beyond the canonical pool. - No backend, no server keys. A static frontend plus your wallet is the entire attack surface.
- Events, never senders. Private transactions are relayed, so the transaction sender is the relayer for everyone. Any per-account view reads pool events instead - attributing by sender would credit every deposit to one address.
- Receipts are inspected. starknet.js resolves
waitForTransactionfor reverted transactions, so success is confirmed from the receipt's execution status. A reverted payout is never shown as confirmed.
Known limits
- · Wallet support is narrow: Ready today, Xverse in progress. Other wallets connect but stay read-only.
- · Recipients must register in the pool themselves, and that registration costs them 6 STRK.
- · Receipts prove attestation, not transfer - see above.
- · The privacy checks are bounded to roughly a day of public pool activity and amount shapes an observer would try first; they cannot see longer history or off-chain context.
- · Shielded balances below the pool fee are effectively stranded until the balance is topped up.
- · The treasury legs are hash-proven on mainnet; the private transfer and split flows ride the same wallet API against the same pool but are not yet exercised by a recorded hash.