Guarantees
Private payouts
Getting paid should not publish what you earn. On Starknet, Sage releases the reward from a Cairo vault and escrows it behind a Poseidon commitment. The worker collects with a one-time link, to any address they name — so the money never lands in the wallet that did the work, and nobody can follow it to their balance.
The problem this exists for
Every ordinary on-chain payout writes a permanent, searchable line: this address earned this much, from this campaign, on this day. For someone whose earnings here are a real part of what they live on, that is an income statement posted publicly, forever — and it is published by the act of being paid, which nobody chooses.
Two halves
- The vault releases. A Cairo
SageVaultlooks the reward up from the mission — the amount is not an argument any caller can pass — and enforces the funding ceiling and per-wallet replay itself. A refused payout answers with a code rather than reverting, so the reason lands on chain where the person refused can read it. - Sage escrows. The money moves into
SageClaimsagainstposeidon(secret), which names nobody. The worker collects with a one-time link. A wallet registered with the STRK20 pool can take it straight into a shielded note; everyone else names an address.
Nothing is needed to collect
claim_to_address is ungated by design — the preimage is the authority, not the caller — so Sage relays the transaction and pays the gas. A worker holding no tokens, who has never used Starknet, whose account is not even deployed, can still be paid. A rail that ends in an asset nobody can move has transferred a number, not capital.
What is private, exactly
Written this precisely because every line is checkable against the chain. A claim that can be falsified with one block-explorer query is worth less than a narrower one that holds.
| Private | Who was on a payout list, and who collected. A commitment names nobody, and the collection leg is keyed by the hash rather than by a person. |
| Private | Where the money ends up. It never lands in the wallet that did the work; the destination is named at collection. |
| Public | Funding — the total and the timing of it. |
| Public | The vault's spending — every amount, mission and decision digest. Auditable, and no longer attributable to a person. |
| Public | The address named at the public collection door, for that leg only. |
The approval record used to name you
PayoutReleased carried the worker as an indexed key, so filtering by a wallet and totalling everything Sage ever paid it was one block-explorer query. A private destination with a public approval record is not a private payout. It now emits an opaque intent hash instead; replay protection is untouched, because it keys on the worker in storage and never needed the event.
Writing the test for that found a second one. PayoutRefused named the recipient too — a public, searchable record of someone being turned down, which on a privacy rail is the last thing that should be attributable. Both events now identify the attempt rather than the person, and the refused party can still find their own refusal because the intent hash derives from their campaign and submission.
Written and tested; not yet the deployed class. The live campaign proving this rail runs on the existing one, and swapping classes underneath it before that is proven would risk the thing it demonstrates.
What stays public on purpose
Every figure Sage reports remains independently verifiable: amounts, payout counts, batch totals and the unclaimed balance are all on chain. Privacy removes the line back to a person, not the receipts. That is the shape a regulator asks for and the one a lender can work with — auditable in aggregate, private per person.
It runs unattended
The decision to pay is the autonomous part. An agent judged a real submission, released the reward, escrowed it and minted the claim link with no human in the loop — confidence 0.92 against a 0.85 threshold, judged by a model that cannot state an amount, because the vault derives it. Both transactions are on Starknet mainnet and can be read today: 0x2b03ed65…49fb released it, 0x68ebf197…8af4 escrowed it.