im_robot Svelte Themes

Im_robot

Reverse-CAPTCHA for AI agents โ€” verify bots, not humans. Multi-framework (React, Vue, Svelte, Web Components). Zero dependencies. TypeScript.

# ๐Ÿค– imrobot

Reverse-CAPTCHA for AI agents โ€” verify bots, not humans.

Live Demo ยท npm ยท Dev.to Article


Why?

Traditional CAPTCHAs prove you're human. But what about the opposite?

As AI agents become first-class web citizens โ€” browsing, booking, purchasing, automating โ€” some systems need to verify their visitors are legitimate AI agents, not humans trying to bypass agent-only access. Think agent-facing APIs, AI-only platforms, or multi-agent authentication.

imrobot flips the CAPTCHA model: it generates deterministic challenge pipelines that are trivial for any LLM or programmatic agent to solve (< 1 second), but impractical for humans to work through manually.

How it works

imrobot generates a pipeline of deterministic string operations (reverse, base64, rot13, hex encode, etc.) applied to a random seed. AI agents parse the structured challenge data, execute the pipeline, and submit the result. Humans would need to manually compute multi-step string transformations โ€” practically impossible without tools.

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

The challenge data is embedded in the DOM via data-imrobot-challenge attribute as structured JSON, making it trivially parseable by any agent.

Install

npm install imrobot

Quick start

React

import { ImRobot } from 'imrobot/react'

function App() {
  return (
    <ImRobot
      difficulty="medium"
      theme="light"
      onVerified={(token) => {
        console.log('Robot verified!', token)
      }}
    />
  )
}

Vue

<script setup>
import { ImRobot } from 'imrobot/vue'

function handleVerified(token) {
  console.log('Robot verified!', token)
}
</script>

<template>
  <ImRobot difficulty="medium" theme="light" @verified="handleVerified" />
</template>

Svelte

<script>
  import ImRobot from 'imrobot/svelte'
</script>

<ImRobot
  difficulty="medium"
  theme="light"
  onVerified={(token) => console.log('Robot verified!', token)}
/>

Web Component (Angular, vanilla JS, anything)

<script type="module">
  import { register } from 'imrobot/web-component'
  register() // registers <imrobot-widget>
</script>

<imrobot-widget difficulty="medium" theme="light"></imrobot-widget>

<script>
  document.querySelector('imrobot-widget')
    .addEventListener('imrobot-verified', (e) => {
      console.log('Robot verified!', e.detail)
    })
</script>

Core API (headless)

import {
  generateChallenge,
  solveChallenge,
  verifyAnswer,
} from 'imrobot/core'

const challenge = generateChallenge({ difficulty: 'medium' })
const answer = solveChallenge(challenge)
const isValid = verifyAnswer(challenge, answer) // true

Screenshot protection

The challenge text is blurred by default and only revealed when the user hovers over it. This defeats screenshot-based attacks (screen capture tools, CDP screenshots, PrintScreen) since the captured image shows only blurred content.

An additional JavaScript shield detects screenshot shortcuts (PrintScreen, Cmd+Shift+3/4/5, Ctrl+Shift+S) and window blur/visibility changes, applying an extra blur layer that overrides even the hover state.

Combined with the hidden nonce (not displayed visually) and TTL expiry, this makes screenshot+OCR workflows ineffective โ€” even if the blur were bypassed, the nonce is missing from the visual output.

Note: AI agents are unaffected โ€” they read challenge data from the DOM, not from the screen.

Using the shield in vanilla JS

The screenshot shield is exported for use outside the bundled components:

import { setupScreenshotShield } from 'imrobot'

const cleanup = setupScreenshotShield((shielded) => {
  // shielded: true when a screenshot attempt is detected
  // automatically resets to false after 1.2s
})

// Call cleanup() to remove event listeners

How agents interact with it

AI agents read the challenge data directly from the DOM via the data-imrobot-challenge attribute โ€” they never need to "see" the visual text, so blur has no effect on them.

  1. Read the challenge from data-imrobot-challenge attribute (JSON)
  2. Execute the pipeline โ€” each operation is a simple string transform
  3. Submit the answer via the input field or programmatically
// Agent reads challenge from DOM (unaffected by blur)
const el = document.querySelector('[data-imrobot-challenge]')
const challenge = JSON.parse(el.dataset.imrobotChallenge)

// Agent solves it (or implement the pipeline yourself)
import { solveChallenge } from 'imrobot/core'
const answer = solveChallenge(challenge)

// Agent fills in the answer and clicks verify
const input = el.querySelector('input')
input.value = answer
input.dispatchEvent(new Event('input', { bubbles: true }))
el.querySelector('button').click()

Operations reference

Operation Description Example
reverse() Reverse the string "abc" โ†’ "cba"
to_upper() Convert to uppercase "abc" โ†’ "ABC"
to_lower() Convert to lowercase "ABC" โ†’ "abc"
base64_encode() Base64 encode "hello" โ†’ "aGVsbG8="
rot13() ROT13 cipher "hello" โ†’ "uryyb"
hex_encode() Hex encode each char "AB" โ†’ "4142"
sort_chars() Sort characters "dcba" โ†’ "abcd"
char_code_sum() Sum of char codes "AB" โ†’ "131"
substring(s, e) Extract substring "abcdef" โ†’ "cde"
repeat(n) Repeat string n times "ab" โ†’ "ababab"
replace(s, r) Replace all occurrences "aab" โ†’ "xxb"
pad_start(len, ch) Pad start to length "abc" โ†’ "000abc"

Configuration

Prop Type Default Description
difficulty 'easy' | 'medium' | 'hard' 'medium' Number and complexity of operations
theme 'light' | 'dark' 'light' Color theme
ttl number 300000 Challenge time-to-live in ms
onVerified (token) => void โ€” Callback on successful verification
onError (error) => void โ€” Callback on failed verification

Difficulty levels

  • easy: 2-3 simple operations (reverse, case, sort)
  • medium: 3-5 operations including encoding and extraction
  • hard: 5-7 operations including replacement and padding

Token

On successful verification, onVerified receives an ImRobotToken:

interface ImRobotToken {
  challengeId: string  // Unique challenge identifier
  answer: string       // The correct answer
  timestamp: number    // Verification timestamp
  elapsed: number      // Time taken to solve (ms)
  signature: string    // Verification signature
}

Contributing

Contributions are welcome! Feel free to open issues for bug reports or feature requests, or submit pull requests.

git clone https://github.com/leopechnicki/im_robot.git
cd im_robot
npm install
npm test

License

MIT

Top categories

Loading Svelte Themes