CAPTCHA proves you're human. HATCHA proves you're not.
HATCHA (H yperfast A gent T est for C omputational H euristic A ssessment) is a reverse CAPTCHA that gates access behind challenges trivial for AI agents but painful for humans β large-number multiplication, string reversal, binary decoding, and more.
Server-side verificationβ answers never reach the client. HMAC-signed tokens, stateless, no database required.** 5 built-in challenge types**β math, string reversal, character counting, sorting, binary decode.** Extensible**β register custom challenge generators at runtime.** Themeable**β dark, light, or auto mode via CSS custom properties.** Framework adapters**β Next.js App Router and Express middleware out of the box.
npm install @mondaycom/hatcha-react @mondaycom/hatcha-server
js
// app/api/hatcha/[...hatcha]/route.ts
import { createHatchaHandler } from "@mondaycom/hatcha-server/nextjs";
const handler = createHatchaHandler({
secret: process.env.HATCHA_SECRET!,
});
export const GET = handler;
export const POST = handler;
js
// app/layout.tsx
import { HatchaProvider } from "@mondaycom/hatcha-react";
import "@mondaycom/hatcha-react/styles.css";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<HatchaProvider>{children}</HatchaProvider>
</body>
</html>
);
}
js
"use client";
import { useHatcha } from "@mondaycom/hatcha-react";
function AgentModeButton() {
const { requestVerification } = useHatcha();
return (
<button
onClick={() =>
requestVerification((token) => {
console.log("Agent verified!", token);
})
}
>
Enter Agent Mode
</button>
);
}
HATCHA_SECRET=your-random-secret-here
Client Server
β β
β GET /api/hatcha/challenge β
ββββββββββββββββββββββββββββββββββΊβ
β β Generate challenge
β β Hash answer
β β HMAC-sign { hash, expiry }
β { challenge (no answer), token }
βββββββββββββββββββββββββββββββββββ
β β
β Agent solves the challenge β
β β
β POST /api/hatcha/verify β
β { answer, token } β
ββββββββββββββββββββββββββββββββββΊβ
β β Verify HMAC signature
β β Check expiry
β β Compare answer hash
β { success, verificationToken } β
βββββββββββββββββββββββββββββββββββ
The answer never reaches the client. The signed token is opaque and contains only a hashed answer + expiry. Verification is stateless β no database needed.
| Type | Icon | What it does | Time limit |
|---|---|---|---|
math |
|||
| Γ | 5-digit Γ 5-digit multiplication | 30 s | |
string |
|||
| β | Reverse a 60β80 character random string | 30 s | |
count |
|||
sort |
|||
| β | Sort 15 numbers, return the k-th smallest | 30 s | |
binary |
|||
| 01 | Decode binary octets to ASCII | 30 s |
import { registerChallenge } from "@mondaycom/hatcha-server";
registerChallenge({
type: "hex",
generate() {
const n = Math.floor(Math.random() * 0xffffff);
return {
display: {
type: "hex",
icon: "0x",
title: "Hex Decode",
description: "Convert this hex number to decimal.",
prompt: `0x${n.toString(16).toUpperCase()}`,
timeLimit: 30,
answer: String(n),
},
answer: String(n),
};
},
});
HATCHA uses CSS custom properties scoped under --hatcha-*
. Override them on any parent element:
[data-hatcha-theme] {
--hatcha-accent: #3b82f6;
--hatcha-accent-light: #60a5fa;
--hatcha-bg: #060b18;
--hatcha-fg: #e4eaf6;
--hatcha-success: #22c55e;
--hatcha-danger: #ef4444;
}
Pass theme="dark"
, theme="light"
, or theme="auto"
to <HatchaProvider>
or <Hatcha>
.
import express from "express";
import { hatchaRouter } from "@mondaycom/hatcha-server/express";
const app = express();
app.use(express.json());
app.use("/api/hatcha", hatchaRouter({ secret: process.env.HATCHA_SECRET! }));
app.listen(3000);
| Package | Description |
|---|---|
@mondaycom/hatcha-core |
@mondaycom/hatcha-react
@mondaycom/hatcha-server
git clone https://github.com/mondaycom/HATCHA.git
cd HATCHA
pnpm install
pnpm build
cd examples/nextjs-app
pnpm dev
Contributions are welcome! See CONTRIBUTING.md for setup instructions and guidelines.