Tap your passport.
Get a real-person ID for any address.
Sigil is an app and a smart contract. The app turns your passport into a unique, opaque ID — one ID per passport, no matter how many addresses you sigilize. The contract lets any other app verify that ID in one on-chain read. They don't integrate anything; they just point users here.
Experimental. Testnet only, on Base Sepolia. RSA-signed passports for now.
Two sides, one mapping
Sigil works the same way whether you're someone who wants their addresses attested, or a protocol that wants to filter for real people.
You tap once. Your addresses get verified.
Your passport produces one stable, opaque ID — the same value every time, for this passport only. Attach it to as many addresses as you own; they all share that ID on-chain. Addresses you leave alone stay anonymous.
- Get your fair share of airdrops, votes, and signups instead of having them diluted by bots.
- No name, DOB, or passport number ever touches the chain.
- Unregister any time.
Tell users to sigilize. Read one mapping.
That's the integration. No SDK to install, no QR code to host, no proof to verify
on your end. Once a user sigilizes their address here, any contract or backend can
read isVerified(address) for personhood and nullifierOf(address)
for sybil dedup.
- Two free Solidity view functions, no API key to manage.
- Each passport produces exactly one nullifier — that's what makes dedup work.
- Proofs are bound to
msg.sender, so they can't be reused on a different address.
How it works
-
01
Tap your passport
Hold the passport against your phone. It reads DG1 and the Security Object over NFC using ICAO 9303 BAC. Nothing is uploaded.
-
02
Prove on-device
A Noir circuit checks the passport's signature chain and derives an opaque per-passport ID. The whole proof runs on the phone in about 5–15 seconds with Mopro.
-
03
Register on-chain
Submit the proof to the registry. After that, any contract or service can look up the address with one Solidity call —
isVerified(address)for personhood,nullifierOf(address)for sybil dedup. Nothing else for them to integrate.
What's actually on-chain
Your passport stays on the phone. The name, date of birth, nationality, and passport number never reach the chain.
Stored on-chain
- An opaque nullifier (one-way hash of your passport secret)
- The address that registered it
- A registration timestamp and expiry, rounded to 90-day buckets
- The zero-knowledge proof itself
Never on-chain
- Name, date of birth, nationality, sex
- Passport number, issue date, the MRZ
- DG1 / SOD bytes, RSA signatures, document images
- Anything that ties back to the actual person
How linkability works
Every address you sigilize with the same passport shares the same on-chain ID. That's the mechanism: same passport, same ID, anywhere it shows up. As a result, anyone reading the chain can tell which of your sigilized addresses are linked to each other. It's a real privacy cost, and it's the trade Sigil makes to keep verification a single mapping read for any protocol. Addresses you don't sigilize are unaffected; they stay pseudonymous.
For protocols
There's barely an integration to show — but here's what gating an action looks like in Solidity.
Personhood check
isVerified(address) returns true when there's a real person currently attested to the address. Drop it in front of an airdrop claim, a faucet, a governance vote, a signup bonus. Addresses without a human behind them get filtered out.
One passport, one share
nullifierOf(address) returns a stable per-person ID. Same value for every address that person has sigilized. Track it in a seen[] set and one passport participates once, no matter how many addresses it controls.
// Gate on personhood, then dedupe by passport.
if (!sigil.isVerified(msg.sender)) revert NotVerified();
bytes32 nullifier = sigil.nullifierOf(msg.sender);
if (claimed[nullifier]) revert AlreadyClaimed();
claimed[nullifier] = true;
// ... continue with the action.
Where it fits
Airdrops
One claim per person, even if they have ten addresses.
DAO voting
One vote per person. No KYC.
Quadratic funding
Matching pools that don't break when someone spins up 50 addresses.
Onboarding rewards
Sign-up bonuses that aren't farmed into the ground.
Reputation & reviews
One review per person, not per fake account.
Rate limiting
Per-person quotas instead of per-address. Spinning up new addresses gets you nowhere.
Off-chain works the same way. A backend can eth_call the registry before issuing an API key or validating a signup. The read is just public state, no roundtrip to the user's device.
Behind the scenes
The honest version: who runs Sigil, who pays for what, and where it goes from here.
Who's running it
Sigil is an open-source side project. The mobile app, the Solidity contracts, the Noir circuit, and the Mopro bridge are all in one GitHub repo. There's no company behind it and no token. The smart contracts are immutable once deployed; nobody can change their behavior, not even the author.
Who pays
Users pay gas to register and unregister, and that's all. isVerified()
and nullifierOf() are view functions, so protocols pay
nothing for reads. There are no usage fees, subscriptions, or per-call charges
today.
Compatibility
Sigil currently verifies passports signed with RSA-2048, the most common scheme on biometric passports today (US, most EU, Japan, and many others). Some countries use ECDSA, which isn't supported yet. The app checks at tap time and tells you if your passport isn't compatible.
Where this is going
Currently testnet only on Base Sepolia. Mainnet deployment is pending until a renewal flow lands in the app and a couple of audit follow-ups are merged. If a protocol fee gets added in a future version, it'll be opt-in and disclosed before anything is shipped.
How to help
Try it on Sepolia, file an issue with what breaks, or send a PR. If you're building a protocol that wants to integrate, the contract addresses and ABIs are in the repo.
Get the app
iOS and Android, soon.