Stop bots without reCAPTCHA or Cloudflare

npm install @powforge/captcha
Or use the CDN drop-in below — no bundler required. Jump to integration flows ↓
Live demo, try it
Pay 3 sats over Lightning, or compute a SHA-256 proof. Either path mints a short-lived token your server can verify.
1Drop-in script tag (zero build step)
Fastest path. Paste two tags into your form page. No npm install, no bundler, nothing to configure on the server until you want to verify the token.
<script src="https://unpkg.com/@powforge/captcha/dist/powforge-captcha.min.js"
data-target="#my-captcha"
data-server="https://captcha.powforge.dev"
data-theme="dark"
data-callback="onVerified">
</script>
<div id="my-captcha"></div>
<input type="hidden" name="pf_token">
Wire the callback
<script>
function onVerified(token, method) {
// token = short-lived JWT (5 min TTL)
// method = 'pow' | 'lightning'
document.querySelector('input[name=pf_token]').value = token;
document.getElementById('submit-btn').disabled = false;
}
</script>
unpkg.com? Public CDN backed by npm. The script is the same artifact published at npmjs.com/package/@powforge/captcha. Subresource Integrity hashes are pinned per release in the package README.
2npm install + ES module
Use this when you have a bundler (Vite, webpack, esbuild, Next, Remix, SvelteKit). You get tree-shaking, types, and version pinning in package.json.
npm install @powforge/captcha
import { PowCaptcha } from '@powforge/captcha';
const captcha = new PowCaptcha({
target: '#my-captcha',
server: 'https://captcha.powforge.dev'
});
captcha.on('verified', ({ token, method }) => {
// token = short-lived JWT (5 min TTL)
// method = 'pow' | 'lightning'
document.querySelector('input[name=pf_token]').value = token;
});
React / Next.js
import { useEffect, useRef } from 'react';
import { PowCaptcha } from '@powforge/captcha';
export function Captcha({ onToken }) {
const ref = useRef(null);
useEffect(() => {
const captcha = new PowCaptcha({
target: ref.current,
server: 'https://captcha.powforge.dev'
});
captcha.on('verified', ({ token }) => onToken(token));
return () => captcha.destroy();
}, []);
return <div ref={ref} />;
}
3Server-side verification
Verify the token on your server before trusting the form submission. Six lines. Works with Express, Fastify, Hono, Next API routes, anything that hands you the request body.
const { verifyToken } = require('@powforge/captcha/verify');
const result = await verifyToken(req.body.pf_token, {
server: 'https://captcha.powforge.dev'
});
if (!result.valid) return res.status(403).json({ error: 'CAPTCHA failed' });
// result.method === 'pow' | 'lightning'
// result.difficulty === 18 (PoW path)
// result.amountSats === 3 (Lightning path)
ES module / TypeScript
import { verifyToken } from '@powforge/captcha/verify';
const result = await verifyToken(token, { server: 'https://captcha.powforge.dev' });
if (!result.valid) throw new Error('Invalid CAPTCHA token');
LLightning-skip tier (L402)
Want visitors to skip the PoW entirely by paying 3 sats? Set l402: true. The widget shows both options side by side: compute the proof for free, or pay over Lightning for instant pass.
// Optional: configure L402 endpoint for Lightning-skip tier
const captcha = new PowCaptcha({
target: '#my-captcha',
server: 'https://captcha.powforge.dev',
l402: true // shows Lightning invoice option alongside PoW
});
The same on script-tag flow:
<script src="https://unpkg.com/@powforge/captcha/dist/powforge-captcha.min.js"
data-target="#my-captcha"
data-server="https://captcha.powforge.dev"
data-l402="true"
data-callback="onVerified">
</script>
✓You're done
- Widget renders on your page
- Token populates
pf_tokenhidden input on verify - Server rejects forms where
verifyTokenreturnsvalid: false - (Optional) L402 enabled, Lightning-skip tier shown
If your widget is not rendering, check the browser console — the script logs the exact mount-target selector it tried. If verification fails on the server, hit https://captcha.powforge.dev/health to confirm the verify endpoint is reachable from your network.
every gate opens with energy. watts or sats.