Documentation
Caligo, end to end.
Caligo is an Orchard-style shielded pool on Base. It gives you a public ERC-20 balance and a private one — hold and move CALIGO with no balance, amount, or counterparty on-chain. This is the complete reference for users and relayer operators.
Introduction
On a public chain every balance, counterparty, and payment time is permanent and searchable. Caligo keeps the public token for liquidity and interoperability, then moves your real position into a shielded pool — a set of encrypted notes — so the chain stops narrating your financial life.
There are four moves, and all of them are private:
- Shield — turn public CALIGO into a private note.
- Send — pay another user with no amount or parties on-chain.
- Unshield — exit to a fresh address through a relayer.
- Earn — receive anonymity-mining rewards already shielded.
Every proof is generated in your browser; your spending key never leaves the device. The same UltraHonk verifiers that ran in your browser check the proof on-chain.
Public and private: your two sides
The single most important thing to understand: Caligo gives you two balances and two addresses. Keeping them separate is what makes privacy possible — if your private side were derived from your public wallet, the two could be linked and the privacy would be gone.
| Public side | Private side | |
|---|---|---|
| Where it lives | your MetaMask account | the shielded pool (notes) |
| Address | your 0x… wallet | your Caligo address npk:encPk |
| Derived from | MetaMask | your 12-word recovery phrase |
| On-chain visibility | balance + history public | nothing visible |
| Holds | public CALIGO + gas (ETH) | your shielded balance |
How value moves between the two
- Shield — move public CALIGO into the pool (public → private).
- Send — pay another Caligo address inside the pool (private → private).
- Unshield — exit to any public
0xaddress (private → public).
Your Caligo address — the long npk:encPk string on the Receive page — is what you share to get paid privately. It is derived from your recovery phrase, not from MetaMask, and it never holds a balance on-chain: it's simply how a payer addresses an encrypted note to you. Your MetaMask address is your ordinary public account; it holds public CALIGO and pays gas, and it's the kind of address you give as the destination of an unshield.
Getting started
Caligo runs on Base Sepolia today. You'll need a browser wallet (MetaMask or another injected wallet) and a little Base Sepolia ETH for gas.
1 · Connect your wallet
Open the app and connect. It switches you to Base Sepolia automatically. This is your public account.
2 · Create your shielded wallet
The app generates a 12-word recovery phrase — your private wallet, separate from MetaMask. Write it down and keep it safe: it is the only way to restore your shielded funds on another device or after clearing your browser. Optionally set a password to encrypt the key at rest in this browser.
3 · Shield some CALIGO
On the Shield tab, enter an amount and confirm. Your public CALIGO becomes a private note in the pool; the proof is built in your browser. Watching your public address afterwards reveals nothing about the shielded balance.
4 · Receive and send privately
Open Receive to get your Caligo address; hand a fresh diversified address to each payer so your incomes can't be correlated. To pay someone, use their Caligo address on the Send tab.
5 · Unshield when you want to go public
Unshield to any 0x address — through a relayer, so the exit isn't linked to you and you pay no gas.
Core concepts
Notes
A note is the unit of private value — a tuple of (value, npk_d, rho): the amount, a diversified owner key, and a one-time secret. When you shield, your CALIGO becomes a note; its commitment Poseidon2(value, npk_d, rho) is inserted into the pool's Merkle tree. The commitment is public; everything it hides is not.
The commitment tree
All commitments live in one incremental Merkle tree of depth 24 (up to ~16.7M leaves) hashed with Poseidon2 — a single shared anonymity set for every user. Each note also emits an ECIES-encrypted copy on-chain, which only the recipient (or a held viewing key) can read.
Nullifiers
To spend a note you reveal its nullifier Poseidon2(sk, rho) — a one-time tag derived from the spending key and the note's secret. The pool records spent nullifiers, so each note is spent exactly once and double-spends are impossible. The nullifier cannot be linked back to the note's commitment.
Diversified addresses
One spending key produces many unlinkable receive addresses (ZEC-style diversification): npk_d = Poseidon2(sk, d). Give a fresh one to each sender; a single key still recovers them all when it scans.
Shield
Shielding deposits public CALIGO into the pool and gives you a private note. The app builds a deposit witness (two dummy inputs, one real output + change), proves it in your browser, then approve + transact from your connected wallet.
After shielding, watching your public address reveals nothing about the shielded balance. The deposit binds to the pool's current root, so it stays valid as the tree grows.
Send
A private send moves value note → note with an external amount of zero. The proof shows value balances; the amount and both parties stay off-chain. The recipient's output is encrypted to their address, so they discover it by scanning with their viewing key. Your change returns to you as a fresh note.
The recipient address is the long Caligo address (npk:encPk) from their Receive page — not a plain 0x wallet. A fixed-length 512-byte memo field rides with the note, so its presence and length are never observable from the on-chain ciphertext.
Unshield
Unshielding withdraws a note to a public 0x address. A zk proof shows you own an input note and that value balances; the nullifier burns it once. The proof binds the recipient, the relayer, and the fee, so nothing can be redirected in transit.
To stay unlinked, the withdrawal is submitted by a relayer (see Run a relayer), not your wallet — so the exit address has no on-chain link to you and pays no gas. You can also self-pay, but that links the exit to your wallet.
Earn — anonymity mining
Anonymity mining pays you for keeping value shielded. It is spend-on-claim: you prove in zk that you hold a note that has existed since an old epoch (binding its age), consume it, and reissue a continuation note worth principal + reward — without revealing which note. Because the held note must be unspent to claim and is consumed by claiming, the reward is bound to your actual holding period.
- Reward ≤
rate × value × age, capped per claim. - The 55% AnonymityHolding emission bucket is the system-wide ceiling.
- Rewards are minted into the pool as fresh private notes — earning never posts to a public scoreboard.
What privacy you get — and its limits
Privacy here is real but conditional, and it grows with how many people use the pool. We'd rather under-promise. In one line: inside the pool, Caligo hides how much you hold and who pays whom — it does not erase patterns you create with amounts and timing, and it can't give you a crowd to hide in until many people are shielded.
Protected (inside the pool)
- Amounts — note values live inside commitments; the chain never shows your balance.
- Ownership — a spend reveals a nullifier that can't be linked back to its note or deposit.
- Sender ↔ recipient — a private send reveals no amount and no parties; payment and change are indistinguishable.
- Receive addresses — diversified addresses are mutually unlinkable.
- Memos — fixed-length and encrypted, so their presence and length aren't observable on-chain.
Not protected (know these)
- Amount correlation — the big one. A distinctive deposit and a later withdrawal of a similar amount can be linked by anyone watching the chain. Inherent to any variable-amount pool. Reduce it: use round amounts, split across notes, leave change shielded.
- Timing. Deposit-then-quick-withdraw of the same value is linkable. Reduce it: let value sit in the pool.
- The public boundary. Your funding address is visible on deposit, and the recipient on a withdrawal if you don't use a relayer. Always exit through a relayer.
- Anonymity-set size. Your privacy is only as strong as the number of plausible notes your spend could be — an adoption property, not a code property. Early on the set is small; the 55% mining bucket exists to grow it.
- Action type. Deposit / send / withdraw are distinguishable by design (the signed external amount). Only amounts and parties are hidden, not the kind of action.
- Network metadata. IP address and RPC query patterns are out of scope for the protocol. Use a VPN/Tor if that's in your threat model.
Tips to maximize your privacy
- Prefer round amounts; avoid one-of-a-kind values.
- Leave change shielded and let value age in the pool before withdrawing.
- Always withdraw via a relayer.
- Hand a fresh diversified address to each sender.
- Don't deposit and withdraw the same amount back-to-back.
Compliance & disclosure
Caligo is neutral by default: the full commitment tree is itself a valid association set, so an honest user is never blocked and needs no ASP. There is no admin backdoor, no freeze, no seize.
- Association sets (Privacy Pools). An Association Set Provider can publish a Merkle root of commitments it considers clean. At withdrawal you can optionally prove your note is in both the full tree and a registered clean subset — proof-of-innocence, without revealing which note. Multiple ASPs can coexist; relayers and exchanges choose which they trust.
- Viewing keys. Hand an auditor or exchange an incoming/outgoing viewing key for full, user-controlled disclosure — read-only, never spend power. Export it from the app's Settings.
Proving
Caligo uses Noir circuits and the UltraHonk proof system (Barretenberg) over the BN254 curve. There are two circuits: transact (a single 2-in / 2-out transaction that covers deposit, transfer, and withdrawal via a signed external amount) and anon_claim (anonymity mining).
- Witnesses and proofs are built in your browser — the spending key never touches a server.
- The exact same verifier contracts check the proof on-chain.
- UltraHonk uses a universal SRS, so there is no per-circuit trusted-setup ceremony.
Proofs are bound to the chain id (and, for transfers/withdrawals, to recipient/relayer/fee via an ext_data_hash), and the pool only accepts its own recent roots — so a proof cannot be replayed on another chain or pool.
Wallet & keys
Spending key & recovery phrase
Your spending key is derived from a 12-word recovery phrase (BIP-39). Write the phrase down: it restores your wallet on any device, and your notes re-sync from the chain when you scan. The key itself never leaves the browser.
Encrypted keystore
Optionally protect the key at rest with a password: scrypt → AES-256-GCM. This guards against someone with access to your browser — it is complementary to the recovery phrase, not a substitute for it.
Viewing keys
An incoming viewing key lets a third party see the notes you received (amount + memo) without spend power. An outgoing viewing key reveals the notes you sent. Both are user-controlled — selective disclosure, not a backdoor.
Emission
CALIGO has no hard cap; supply is governed by a disinflationary halving curve plus a perpetual tail. Emission accrues per epoch and is split by fixed, immutable weights across three buckets:
| Bucket | Share | For |
|---|---|---|
| Anonymity holding | 55% | users who keep value shielded |
| Relayer ops | 15% | relayers / operators, by work |
| Treasury | 30% | audits, liquidity, integrations, proving infra |
Each bucket can only mint up to what time has unlocked for it, which rate-limits every reward manager. Reference parameters: base 144,000 CALIGO/epoch, halving ~every 2 years, perpetual tail 4,800/epoch, genesis 3,000,000. There is no LLM/useful-work pillar — unverifiable on-chain, so it is treasury-funded if it ever matters.
Run a relayer
A relayer submits a user's withdrawal so the exit is paid and broadcast by the relayer, never the user's wallet — so there is no on-chain link between the user and the exit. The user proves the withdrawal in their browser and hands the proof to a relayer over HTTP; the relayer broadcasts it and fronts the gas.
Why relaying is trustless
The pool binds recipient, relayer, fee, chainid, root, amount, nullifiers and output commitments into the proof. On a withdraw, settlement does not use msg.sender — the pool pays the recipient and relayer from the proof-bound addresses. So a relayer is pure transport: it cannot redirect the recipient, change the amount, or inflate its fee; it cannot relay a deposit (that would pull tokens from itself, so the service refuses it). The worst it can do is censor (not submit), and the app falls back to another relayer or a self-paid exit.
Two layers: relaying vs the registry
Don't conflate them. Relaying is open to anyone — the pool accepts any relayer address bound in a proof, so you can self-host a relayer for your own exits with no permission and no bond. The registry + rewards are opt-in: register a CALIGO bond in the RelayerHub to be discovered by the app and eligible for the RelayerOps bucket. Because exits are free, the only profit incentive is the bucket, which needs a bond.
Quick start
You need Node ≥ 20 and a funded key with Base Sepolia ETH for gas.
You now have a working relayer. It dry-runs every request with staticCall before broadcasting, so doomed transactions (spent nullifier, unknown root, bad proof) are rejected cleanly instead of burning gas.
HTTP API
| Endpoint | Returns |
|---|---|
GET /info | { relayer, feeWei, chainId, pool } |
POST /relay | { txHash } — after 1 confirmation |
The service validates that the proof names this relayer, refuses deposits (extAmount > 0), and requires the exit fee to meet its advertised floor before spending any gas.
Be discoverable + earn (register)
Registering puts your {address, endpoint} in the on-chain RelayerHub the app reads, and makes you eligible for rewards. It needs a bond ≥ MIN_BOND (currently 1000 CALIGO on testnet). Easiest is auto-register on boot:
On boot the service reads MIN_BOND, approves the bond, and calls registerRelayer(feeRecipient, endpoint, bond) — best-effort, never blocking serving. The app validates that your /info reports the same address you registered (anti-spoof), so point your endpoint at a server you control. The bond is withdrawable (no slashing in this MVP); its anti-Sybil value is the lockup cost per identity.
Economics — paid by the bucket, not per-exit
Exits are free for users (FEE_WEI = 0 by default). Relayers are paid by the RelayerOps bucket (15% of emissions), per epoch, in proportion to verified work — not by per-exit fees. A permissioned scorer finalizes each epoch within the emission schedule and a per-relayer cap (default 20%, hard max 50%); then anyone calls claim(epoch, relayer) and the reward is minted to your feeRecipient. So there is no fee market and no cheapest-relayer race — the app picks one relayer at random per exit, which spreads the work and is a privacy win (no single relayer sees most exits).
Operations & security
- Gas — keep the relayer key funded with Base Sepolia ETH; the service estimates per relay and falls back to 3M.
- One key, one process — submissions are serialized per key (one nonce lane); scale with more keys, not more processes on one key.
- Key hygiene — the relayer key is hot (it signs every relay). Use a dedicated key; never reuse a mainnet/deployer key. It only needs ETH for gas + (optionally) the CALIGO bond.
- You can't steal — the proof binds everything. The only abuse vectors are censorship and self-griefing (submitting doomed txs at your own gas cost); neither touches user funds.
Security & trust model
Caligo is built to be boring where it matters.
- No admin minting. The token has no admin mint. Only the EmissionController can mint, and only up to the schedule. No owner switch. No arbitrary minting.
- Immutable core. Pool and circuits are not patched in place — new designs ship as new versions, never edits to live private state.
- Proofs bound to context. Each proof is bound to the chain id; the pool checks its own roots; every note spends exactly once.
- Backed reward notes. Shielded rewards are minted into the pool as real CALIGO before the note is inserted, so outstanding value is always fully backed.
- Governance. At mainnet, mint-governing roles move to a Safe + Timelock with a delayed, reversible admin handover — no single hot key can rug or brick the protocol.
Contracts
Deployed on Base Sepolia (chain id 84532). Addresses are environment-specific; the app and relayer mirror the deployment manifest.
| Contract | Address |
|---|---|
| CaligoToken | 0xc529EC6eb983949682cbA62B7e099d67b5168931 |
| EmissionController | 0xb62bB6B133842250bA939E1014015Bb6bD155cfD |
| ShieldedPool | 0x10895B71188a5Cd312E12D1785965B623322dfd7 |
| TransactHonkVerifier | 0x568877f34683f3b993513c8899f1D2eBb758F25B |
| AnonClaimHonkVerifier | 0xdc11a8a90500E102F3fB6F63a1822922c964F4e7 |
| Poseidon2 | 0xbec60A165cD0FDEa1516a9Fb65b871756E4250dC |
| AssociationSetRegistry | 0x2Ac0c75E7cdA5a0cEd4AF01850B3eF518c7987f7 |
| AnonymityMiningController | 0xc487e8438bC066613033Cb6913F529cD44177f0E |
| RelayerHub | 0x64B3Ed89C48B7d73b438228c0aab83AFfAA3e2E5 |
| RelayerRewardController | 0xdE9641Ed677a8adc42eB08E84142bcBb1224d6c4 |
FAQ
Why do I have two addresses?
Your MetaMask 0x address is your public account (public CALIGO + gas). Your Caligo address (npk:encPk) is your private identity, derived from your 12-word recovery phrase and unlinkable to MetaMask. Share the Caligo one to receive privately; use a 0x address as the destination of an unshield.
Which address do I share to get paid?
Your Caligo address from the Receive page — the long npk:encPk string, never your plain 0x wallet. Hand a fresh diversified address to each payer so your incomes can't be correlated.
Is my balance ever visible?
No. Once shielded, the value lives as an encrypted note in a shared anonymity set. Your public address shows nothing about the shielded balance.
What if I clear my browser?
Restore from your 12-word recovery phrase on any device; your notes re-sync from the chain. Without the phrase, the funds cannot be recovered.
Do I pay gas or a fee to exit?
No — a relayer fronts the gas and is reimbursed from the RelayerOps emission bucket. Exits are free for the user.
Can the team mint or freeze my funds?
No admin mint exists, and the pool is immutable. Disclosure is opt-in via viewing keys; there is no hidden backdoor, freeze, or seize.
Is it audited?
Not yet — audit pending. Caligo is currently a testnet protocol on Base Sepolia. Do not use real funds.