Skip to content

zkemail/zkemail-verify-bot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zkemail-verify-bot

A Discord bot that grants roles to users based on ZK-verified ENS text records.

Built on top of zkemail's on-chain infrastructure: when a user runs /verify vitalik.eth, the bot reads verifyTextRecord(namehash("vitalik.eth"), "com.discord", <their-discord-username>) on the Sepolia LinkHandleEntrypoint for Discord. A true return is cryptographic proof that a ZK-verified email proof was submitted binding that ENS name to that Discord handle — not just a wallet-owner self-attestation. The bot then assigns the server's configured "verified" role.

See ens.zk.email for the verification flow users complete.

Quickstart

1. Create a Discord application

  1. Go to https://discord.com/developers/applications and click New Application.
  2. Bot tab → Reset Token → copy the token (this is DISCORD_TOKEN).
  3. General Information → copy the Application ID (this is DISCORD_CLIENT_ID).
  4. Bot tab → under Privileged Gateway Intents, leave them all off (we don't need any).
  5. OAuth2 → URL Generator → check bot and applications.commands scopes. Under bot permissions, check Manage Roles and Send Messages. Copy the generated URL and open it to invite the bot to your test server.

2. Configure

cp .env.example .env
# edit .env with your tokens

For a fast dev loop, set DISCORD_DEV_GUILD_ID to your test server's ID — slash commands register instantly per-guild instead of taking up to an hour globally.

3. Install + register commands + run

npm install
npm run register   # uploads slash commands to Discord
npm run dev        # starts the bot

Usage

As a server admin (one-time)

/verify-config role role:@verified

Pick a role that's below the bot's own role in the role list (so the bot can assign it). Make sure the bot has the Manage Roles permission.

As a server member

/verify ens:vitalik.eth
  • If vitalik.eth has a ZK-verified com.discord text record matching your Discord username, you get the verified role instantly.
  • If not, the bot DMs you a deep link to ens.zk.email with step-by-step instructions to verify (takes ~2 minutes). After verifying, run /verify again.

Other commands

  • /me — show your linked ENS name (if any) in this server.
  • /whois user:@someone — show another user's linked ENS name.

Safety model

The bot reads verifyTextRecord on the platform's LinkHandleEntrypoint contract. That function only returns true if a valid ZK proof was submitted through the entrypoint's gated write path. Plain resolver.setText(...) writes by the ENS owner are stored in a different contract entirely (the standard ENS resolver) and are invisible to this check.

This means:

  • An attacker with the wallet that owns an ENS cannot fake a verified Discord handle on that ENS without also compromising the email account that receives Discord's password-reset mails.
  • Unicode-spoofed Discord handles (e.g., vitalik with a Cyrillic а) cannot be smuggled past the bot, because the canonical handle string is extracted from the actual email Discord sends.
  • The bot does not trust the ENS owner's self-attestation of their Discord handle — only ZK-proven assertions count.

Repo layout

src/
├── chain/            # viem read of verifyTextRecord
├── discord/          # discord.js client + slash commands
├── storage/          # better-sqlite3: per-guild config + per-user links
├── config.ts         # env loader
└── index.ts          # bot entrypoint
scripts/
├── register-commands.ts   # uploads slash command schemas
└── test-verifier.ts       # manual on-chain read test

Deploy

The bot uses discord.js's persistent gateway WebSocket, so serverless platforms (Cloudflare Workers / Vercel / Lambda) won't work as the primary host. You want an always-on worker with a writable disk for the SQLite DB.

Render (recommended) — one-click via Blueprint

A render.yaml Blueprint is in the repo. It provisions a Background Worker on the starter plan with a 1 GB persistent disk mounted at /data. Cost: ~$7/mo worker + ~$0.25/mo disk.

Deploy to Render

Or manually:

  1. Push the repo to your GitHub.
  2. Render dashboard → New +Blueprint → connect this repo → Apply.
  3. In the service settings, set the two secrets the blueprint left blank:
    • DISCORD_TOKEN — your bot token from the Discord Developer Portal.
    • DISCORD_CLIENT_ID — the Application ID.
  4. Wait for the first deploy to finish.
  5. Open the service's Shell tab and run once: npm run register. This uploads slash commands to Discord (idempotent — safe to re-run after any command-shape change).
  6. Invite the bot to your test server using the OAuth2 URL from the Developer Portal (bot + applications.commands scopes, with Manage Roles and Send Messages permissions).

Other hosts

  • Railway (~$5-7/mo): same model — Background Worker + persistent volume.
  • Fly.io (~$2-5/mo): cheapest "real" platform; flyctl deploy from a small fly.toml.
  • VPS (Hetzner / DO, ~$4-6/mo): npm ci && npm run register && npm start under systemd or PM2. Cheapest but you maintain the box.

In every case: ensure DB_PATH points to a persistent volume so user verifications survive restarts.

What's next (v1)

  • Read additional verified text records (twitter, github, reddit, email) and offer per-platform role gates.
  • Support web2 users via paytox-style auto-generated subdomains (<discord-username>.discord.zkemail.eth) — no wallet, no existing ENS required.
  • Periodic re-checks to revoke roles if claims become invalid.
  • /tip @user deep-linking to paytox.

See the plan in /Users/benceharomi/.claude/plans/our-goal-is-to-frolicking-knuth.md.

About

Discord bot that grants roles from ZK-verified ENS text records (zkemail). Identity reader, not identity holder.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors