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.

Status: live on Base Sepolia (testnet). Proofs are verified on-chain; the protocol is not yet audited — audit pending before mainnet. Do not use real funds.

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 sidePrivate side
Where it livesyour MetaMask accountthe shielded pool (notes)
Addressyour 0x… walletyour Caligo address npk:encPk
Derived fromMetaMaskyour 12-word recovery phrase
On-chain visibilitybalance + history publicnothing visible
Holdspublic 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 0x address (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.

Connecting, changing, or disconnecting MetaMask never changes your Caligo address — that depends only on your 12-word recovery phrase. The two addresses are deliberately unlinkable.

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.

Back up your phrase first. Clearing your browser without the recovery phrase means the shielded funds can never be spent. The phrase is the only recovery path.

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.

# CLI equivalent node scripts/wallet.mjs deposit 100000000000000000000 # 100 CALIGO node scripts/wallet.mjs scan node scripts/wallet.mjs balance

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.

node scripts/wallet.mjs transfer 30000000000000000000 <recipient-caligo-address> "memo"

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.

# self-paid (links the exit to your wallet) node scripts/wallet.mjs withdraw 20000000000000000000 <to-0x-address> # gasless via a relayer (unlinked) RELAYER_URL=https://relayer.caligo.cash node scripts/wallet.mjs withdraw 20000000000000000000 <to-0x-address>

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.
Caligo is Orchard-inspired, not Zcash: it borrows the concepts (shielded notes, nullifiers, a commitment tree, viewing keys, diversified addresses) but uses EVM-practical cryptography (Noir/UltraHonk + Poseidon2). It is not unconditional anonymity — the protocol gives you the tools; using them well is on you.

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.

Backup matters. Clearing the browser without the recovery phrase means the shielded funds can never be spent. The phrase is the only recovery path.

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:

BucketShareFor
Anonymity holding55%users who keep value shielded
Relayer ops15%relayers / operators, by work
Treasury30%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.

cd relayer npm install cp .env.example .env # set RELAYER_PRIVATE_KEY (a funded testnet key — never a mainnet key) npm start # serves on :8788

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

EndpointReturns
GET /info{ relayer, feeWei, chainId, pool }
POST /relay{ txHash } — after 1 confirmation
// POST /relay — request body { proof, // 0x… UltraHonk proof inputNullifiers: [nf0, nf1], outputCommitments:[cm0, cm1], extData: { root, associationRoot, extAmount, recipient, relayer, fee }, encOut0, encOut1 // encrypted note blobs (0x for none) }

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:

# .env AUTO_REGISTER=true PUBLIC_URL=https://relayer.example.com # the URL the app reaches you at BOND_WEI= # empty = exactly MIN_BOND

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.
Audit pending. Mainnet ships only after an independent Solidity audit and a separate Noir circuit review. Until then, treat Caligo as beta — do not shield more than you would accept losing.

Contracts

Deployed on Base Sepolia (chain id 84532). Addresses are environment-specific; the app and relayer mirror the deployment manifest.

ContractAddress
CaligoToken0xc529EC6eb983949682cbA62B7e099d67b5168931
EmissionController0xb62bB6B133842250bA939E1014015Bb6bD155cfD
ShieldedPool0x10895B71188a5Cd312E12D1785965B623322dfd7
TransactHonkVerifier0x568877f34683f3b993513c8899f1D2eBb758F25B
AnonClaimHonkVerifier0xdc11a8a90500E102F3fB6F63a1822922c964F4e7
Poseidon20xbec60A165cD0FDEa1516a9Fb65b871756E4250dC
AssociationSetRegistry0x2Ac0c75E7cdA5a0cEd4AF01850B3eF518c7987f7
AnonymityMiningController0xc487e8438bC066613033Cb6913F529cD44177f0E
RelayerHub0x64B3Ed89C48B7d73b438228c0aab83AFfAA3e2E5
RelayerRewardController0xdE9641Ed677a8adc42eB08E84142bcBb1224d6c4

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.