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.

For Users

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.
For Protocols

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

  1. 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.

  2. 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.

  3. 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

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.

Sybil resistance

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.

Solidity Standard integration pattern
// 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.