How a private release works, start to finish
A vault is one hash on a public chain plus the rules for when it opens. Who set it up, who it pays and who can recover it stay with the people involved. They get proven when it matters, and never shown.
The whole lifecycle
vaultId = Poseidon2(ownerSecret, salt)Claimed(nullifier, share, H(dest))Your vault is a commitment, not an account
When you create a vault, your device generates a random owner secret and a salt and hashes them with Poseidon2, a hash function designed to be cheap inside zero-knowledge circuits:
vaultId = Poseidon2(ownerSecret, salt)
That vaultId is the only identifier the coordination chain ever sees. There is no owner address, no beneficiary list and no guardian list stored in PrivacyVaultRegistry. The registry has no admin, no pause switch and no upgrade path, and it holds no funds.
Alongside the commitment you register a policy:
- an inactivity period (2 days to 10 years), an unlock time, or both;
- a beneficiary root: the Merkle root of each beneficiary’s hashed secret and share;
- optionally a guardian root and a threshold k for social recovery.
Those parameters are public. The identities behind the roots are not. See what each party can learn.
Every action is a zero-knowledge proof
The registry never checks who sent a transaction. It checks a proof. Three Noir circuits cover every action. Their UltraHonk proofs are generated locally in your browser and verified by on-chain verifier contracts.
- owner_auth
- Proves you know the secret behind
vaultId, bound to one specific action (create, reconfigure, or check in for today’s epoch), so a proof can’t be replayed for anything else. - claim
- Proves a beneficiary is a leaf in the beneficiary tree with a given share, without revealing which leaf. A nullifier prevents double claims, and the payout destination is bound into the proof.
- guardian_vote
- Lets a guardian vote anonymously to move the vault to a new owner commitment. One vote per guardian per recovery round.
Because the registry ignores msg.sender, anyone can submit your proof. A relayer will do that for you so your wallet is never linked to your vault. Until it ships, the submitting address is visible on-chain like any transaction sender.
A vault opens on a date, or after silence
A vault becomes releasable when either condition in its policy holds:
- Inactivity: no owner check-in for longer than the inactivity period. Check-ins are proofs over a one-day epoch.
- Unlock time: the timestamp you set has passed.
There is no oracle deciding whether you are alive, no document upload and no AI. If you are still around, check in. If you set an unlock time, the vault opens at that time.
Each beneficiary then generates a claim proof. The registry checks it against the stored beneficiary root and records the nullifier, so the same leaf can never claim again. It then emits a Claimed event with the share and a hash of the payout destination.
Once a vault has paid its first claim, the owner can no longer reconfigure it and guardians can no longer recover it.
The registry holds no funds. Independent keepers watch for Claimed events and co-sign the payout with threshold keys on whichever chain holds the assets. A payout can optionally go through the Zcash shielded pool so the vault and the beneficiary can’t be linked. See chains.
If you lose your secret, your guardians can help. We can’t.
If you configured guardians, any k of n of them can each submit an anonymous vote to move the vault to a new owner commitment you generate. When the k-th vote lands, the vault rotates and keeps its policy. A single check-in or reconfiguration from the current owner cancels any recovery round in progress, so guardians cannot quietly take over a vault whose owner is still active.
We cannot recover vaults. We never hold the secret, and the registry has no admin.