DEV Community

Leo Pechnicki
Leo Pechnicki

Posted on

Why I Built a CAPTCHA That Only Bots Can Solve

Traditional CAPTCHAs block bots. I built something that does the opposite.

The Problem

As AI agents become first-class web users, we need identity verification that works for them, not against them. Whether you're building an AI-agent-only API, a bot portal, or testing agent capabilities, you need a way to verify that a client is actually an AI.

Introducing imrobot

imrobot is a Reverse-CAPTCHA — it generates challenges that only programmatic agents can solve. It creates pipelines of deterministic string operations (reverse, base64, rot13, hex encode, etc.) applied to a random seed. Agents parse the structured data and execute the pipeline. Humans would need to manually compute multi-step transformations — practically impossible without tools.

How It Works

seed: "a7f3b2c1d4e5f609"
  1. reverse()
  2. to_upper()
  3. base64_encode()
  4. substring(0, 12)
  5. rot13()
Enter fullscreen mode Exit fullscreen mode

The challenge data is embedded in the DOM as JSON via a data-imrobot-challenge attribute. Agents read this directly — they never need to "see" the visual text, so blur protection doesn't affect them.

Framework Support

imrobot works everywhere:

  • React: <ImRobot difficulty="medium" onVerified={handleToken} />
  • Vue: <ImRobot @verified="handleVerified" />
  • Svelte: <ImRobot on:verified={handleVerified} />
  • Web Components: <imrobot-widget difficulty="medium"></imrobot-widget>
  • Core API (headless): generateChallenge()solveChallenge()verifyAnswer()

REST API Server

The project also includes a zero-dependency REST API server for backend-only verification — no UI needed:

Endpoints:

  • POST /api/v1/challenge — Generate a challenge
  • POST /api/v1/solve — Solve (reference/testing)
  • POST /api/v1/verify — Verify an answer
  • GET /api/v1/health — Health check

Security Features

  • Challenge text is blurred by default (revealed on hover)
  • JavaScript shield detects screenshot shortcuts
  • Hidden nonce prevents OCR/screenshot workflows
  • TTL expiry makes captured challenges useless
  • Agents are unaffected — they read from the DOM, not the screen

Get Started

Check out the project on GitHub: leopechnicki/im_robot

Contributions and feedback welcome!

Top comments (0)