Built for Polkadot People Chain · DIM2 attestation prototype

Prove DKIM-backed email ownership.
Add a Polkadot credential signal.

Zero-knowledge proof of email ownership for a privacy-preserving credential signal. No app-side issuer in the trust path, no raw email contents revealed. The trust root is the cryptographic DKIM signature your email provider already puts on every email.

A Polkadot-native zkEmail-based attestation-source prototype for Polkadot People / Individuality evaluation, designed to plug into DIM2 via a FRAME pallet and a Groth16 verifier already live on Polkadot Asset Hub mainnet.

Email-scale
Low-friction
attestation path
No new
Credential issuer
for email proof
~14s
Observed real-circuit proof
(Node CLI)
23
Pallet source
tests

The DIM2 problem

Polkadot's People / Individuality system can use multiple credential sources. The named candidates commonly introduce either an issuer, an attestor, or a service-specific verifier in the trust path.

Trusted issuer

KILT SocialKYC

Server verifies the account and signs a credential. The trust path includes that issuer, so the verifier must trust KILT's attestation semantics in addition to email cryptography.

Live attestor

Reclaim Protocol

TLS attestor nodes co-witness your HTTPS session in real time. That model is strong for arbitrary web data, but it is not the native trust root for DKIM-signed email.

✓ No live attestor

zkAttest

The trust root is the email provider's DKIM signature, a cryptographic fact that exists before zkAttest touches it. No app-side attester or issuer is added. The proof verifies on-chain.

How it works

Five steps from your inbox to a People Chain-aligned attestation. The production goal is local proving, so raw email content does not leave the user's device.

1

Receive a challenge email

In the production flow, the dApp will trigger a relayer to send a challenge email to your address; you click a link and your browser downloads the raw .eml. For today's demo, you can drag-drop any existing DKIM-signed email from your inbox.

Why this works: major providers such as Gmail, Outlook, and Yahoo commonly sign outgoing email with a private RSA key (DKIM, RFC 6376). That signature is in the email you just received.

2

Browser generates a ZK proof

The production prover will run the zkEmail Circom circuit locally via WASM. The deployed CLI path already proves this circuit on Polkadot Asset Hub; the browser path is the next integration milestone. The circuit proves:

  • ✓ You possess an email with a valid DKIM signature from this specific provider
  • ✓ The signature verifies against the provider's registered public key
  • ✓ The public nullifier can be checked on-chain to prevent replay

The email body, recipient, message ID, and full headers are never revealed.

3

On-chain proof verification

The Groth16 proof is verified on Asset Hub by the deployed Verifier.sol contract (compiled to PolkaVM via Parity's resolc compiler). BN254 pairing precompiles confirm the mathematics in a single transaction.

4

Nullifier stored, replay impossible

The nullifier (a hash of the DKIM signature bytes, unique to the signed message) is stored on-chain in pallet-zkattest. Once recorded, the same proof cannot be reused.

People Chain integration target

pallet-zkattest is designed to call the People Chain personhood interface once it is finalized. The goal is a candidate People Chain attestation path without government ID, in-person ceremony, or a trusted issuer for email ownership.

Live demo

Drop any .eml file to see your DKIM signature extracted and validated in the page. The demo does not upload the file.

📧
Drop your .eml file here or click to browse
Export any email as .eml from your mail client. Nothing is uploaded.
✓ Toy circuit, real Groth16

Try in-browser ZK proving

This is the same Groth16 proving system zkAttest uses for email proofs — running here on a small demo circuit to show real ZK performance in your browser. No proving server. Inputs stay in the page.

The demo circuit proves you know two numbers a, b such that iterating x ← x² + b 1000 times starting from a² + b yields the public output. The full zkEmail circuit replaces this with DKIM signature verification + regex matching.

Technical status

What's built, what's in progress, and what's next.

✓ Complete

Groth16 Verifier · PolkaVM

A snarkjs-generated Groth16 Verifier.sol compiles to valid PolkaVM bytecode via Parity's resolc 1.1.0 with zero source modifications. PVM\0 magic confirmed. Deployed and proof-verified end-to-end on Polkadot Asset Hub mainnet.

Deploy pending Run node spike/deploy-substrate.mjs against Asset Hub to populate.
✓ 23 source tests

FRAME Pallets

pallet-dkim-registry — governance-gated on-chain key registry. 5 tests.

pallet-zkattest — attest/revoke extrinsics, nullifier storage, DIM2 integration target. 18 tests.

The zkAttest count includes 5 pallet tests, evm_abi (Solidity calldata encoder/decoder, 8 tests), and ReviveVerifier (ABI bridge to deployed verifier contract, 5 tests).

View source ↗
✓ Prototype

DKIM Scraper

Queries DNS and archives active DKIM public keys before they rotate. Confirmed pulling live RSA keys from Gmail, Yahoo, ProtonMail, SendGrid, and others. Intended to feed governance proposals to pallet-dkim-registry.

In progress

Prover SDK

TypeScript/ESM package (@zkattest/prover). DKIM header parsing, EML splitting, and @zk-email/helpers integration complete. The on-page demo uses a toy circuit; the real zkEmail circuit artifacts are hosted on Cloudflare R2.

R2 pending Real-circuit artifacts: set ZKEMAIL_BASE_URL in web/index.html.

Trust model at a glance

Component Trust assumption Failure mode
DKIM public key Email provider's DNS (e.g. Gmail) Provider compromised (key rotation mitigates)
Key registry Polkadot governance Malicious governance update (OpenGov timelock)
ZK circuit Upstream zkEmail circuits/helpers + open source review Circuit bug (independent audit planned)
Proof verifier BN254 pairing precompile (PolkaVM) Precompile bug (Parity-maintained)

Known privacy tradeoff: the current nullifier design is derived from DKIM signature bytes, which are unique per email message. The email provider could in principle correlate that nullifier to the original message. A production DIM should document or improve this construction before deployment.

zkAttest vs. zkEmail

zkAttest uses zkEmail's Circom circuits and TypeScript helpers, but is a different project with a different target. zkEmail is upstream DKIM proof infrastructure; zkAttest is the Polkadot-native People Chain integration prototype around that infrastructure.

zkAttest (this project) zkEmail (upstream)
Where the proof verifies Polkadot Asset Hub via pallet-revive (PolkaVM); later pallet-zkattest on People Chain Application-specific smart contracts, primarily EVM-oriented today
Verifier compiler Solidity → PolkaVM via resolc 1.1.0 (Parity) Solidity → EVM via solc
Wallet integration Polkadot.js / Talisman / SubWallet (injected web3) MetaMask / WalletConnect
DKIM key trust root On-chain pallet-dkim-registry (governance-curated) + DKIM scraper Application-specific key and verifier management
Integration target A candidate email-ownership attestation path for Polkadot's People/Individuality stack An ERC-721 / contract state (e.g. proof-of-twitter NFT)
Circuit source @zk-email/circuits EmailVerifier (reused as-is) ↑ same — we don't fork or reimplement
Reference circuit demo People / Individuality attestation prototype for Polkadot proof-of-twitter

Why this matters: Polkadot's People / Individuality system could benefit from at least one privacy-preserving DIM2 (Decentralized Individuality Mechanism, tier 2) credential source. The named candidates on Polkadot today - KILT SocialKYC, eIDAS, Reclaim Protocol - include either an issuer, attestor, or service-specific verifier in the trust path. zkEmail-based credential signals exist elsewhere, notably the ZK Email Stamp on Human Passport for Ethereum, which proves Amazon/Uber receipts as a proxy for economic activity, but no equivalent Polkadot-native email-ownership path is established. zkAttest targets that gap, made possible by zkEmail's primitives plus pallet-revive's PolkaVM mainnet availability.