JSPM

@powforge/captcha

0.2.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 126
  • Score
    100M100P100Q78051F
  • License MIT

Self-hosted proof-of-work CAPTCHA. Privacy-first reCAPTCHA alternative with zero tracking.

Package Exports

  • @powforge/captcha
  • @powforge/captcha/verify

Readme

@powforge/captcha

Self-hosted reCAPTCHA alternative using proof-of-work. No tracking, no cookies, no external dependencies.

Your users prove they're human by computing SHA-256 hashes in their browser. No data leaves your site. No third-party scripts. Works offline once loaded.

Quick Start

1. Install

npm install @powforge/captcha

Or use a script tag (no build step needed):

<script src="https://unpkg.com/@powforge/captcha/dist/powforge-captcha.min.js"></script>

2. Add the widget

<form action="/submit" method="POST">
  <input type="text" name="email" placeholder="Email">

  <!-- CAPTCHA mounts here -->
  <div id="pow-captcha"></div>
  <input type="hidden" name="pf_token">

  <button type="submit">Submit</button>
</form>

<script src="powforge-captcha.min.js"
        data-target="#pow-captcha"
        data-server="https://captcha.powforge.dev"
        data-theme="dark"></script>

3. Verify server-side

const { verifyToken } = require('@powforge/captcha/verify');

app.post('/submit', async (req, res) => {
  const result = await verifyToken(req.body.pf_token, {
    server: 'https://captcha.powforge.dev',
  });

  if (!result.valid) {
    return res.status(403).json({ error: 'CAPTCHA failed' });
  }

  // User verified - process form...
});

How It Works

  1. Your page loads the widget (5KB gzipped)
  2. Widget fetches a challenge from the verification server
  3. A Web Worker mines SHA-256 hashes until it finds one with enough leading zero bits
  4. The solution is sent to the server for verification
  5. Server returns a signed token your backend can validate

Default difficulty: 16 bits (~65,536 hashes, ~4 seconds on average hardware). Adjustable per-challenge.

The proof-of-work is real energy expenditure. Bots pay a real cost. Humans wait a few seconds. No behavioral tracking, no fingerprinting, no cookies.

API

Script Tag (zero config)

<script src="powforge-captcha.min.js"
        data-target="#my-div"
        data-server="https://captcha.powforge.dev"
        data-theme="dark"
        data-difficulty="16"
        data-callback="onVerified">
</script>
Attribute Default Description
data-target (required) CSS selector for mount element
data-server https://captcha.powforge.dev Verification server URL
data-theme dark dark or light
data-difficulty 16 Leading zero bits required
data-callback none Global function called with (token, method)

ES Module / npm

import { PowCaptcha } from '@powforge/captcha';

const captcha = new PowCaptcha({
  target: '#pow-captcha',
  server: 'https://captcha.powforge.dev',
  theme: 'dark',
  difficulty: 16,
});

captcha.on('verified', ({ token, method }) => {
  console.log('Token:', token);
});

captcha.on('progress', ({ nonce, elapsed, percent }) => {
  console.log(`${percent.toFixed(0)}% complete`);
});

captcha.on('error', (err) => {
  console.error('CAPTCHA error:', err);
});

Methods

Method Description
start() Fetch challenge and begin solving
reset() Destroy current work, fetch new challenge
getToken() Returns token string or null
destroy() Terminate worker, free resources
on(event, fn) Listen for events

Events

Event Payload Description
verified { token, method } Challenge solved and verified
progress { nonce, elapsed, percent } Mining progress update
error Error Challenge fetch or solve failure

DOM Events

The widget also fires a powforge:token CustomEvent on window:

window.addEventListener('powforge:token', (e) => {
  console.log(e.detail.token, e.detail.method);
});

Hidden Input

If your form contains <input type="hidden" name="pf_token">, it is auto-filled when the CAPTCHA is solved.

Server Verification

const { verifyToken } = require('@powforge/captcha/verify');
// or: import { verifyToken } from '@powforge/captcha/verify';

const result = await verifyToken(token, {
  server: 'https://captcha.powforge.dev',  // your self-hosted URL
  timeout: 5000,                            // ms
});

// result: { valid: true, method: 'pow', issued_at: '...', expires_at: '...' }
// or:     { valid: false, reason: 'expired token' }

Comparison

Feature PowForge reCAPTCHA hCaptcha ALTCHA Turnstile
Privacy No tracking Google tracking Fingerprinting No tracking Cloudflare tracking
Self-hosted Yes No No Yes No
Dependencies 0 Google JS hCaptcha JS 0 Cloudflare JS
Method SHA-256 PoW ML behavior ML + labeling PoW ML behavior
Bundle size ~8KB gz ~150KB ~120KB ~30KB ~80KB
Open source MIT No No MIT No
Cookies None Yes Yes None Yes
Works offline Once loaded No No Once loaded No
Lightning skip Yes No No No No
Cost Free / self-host Free tier + paid Free tier + paid Free / self-host Free tier

Self-Hosted Server

Run your own verification server:

# Clone the server
git clone https://gitlab.com/powforge/captcha.git
cd captcha

# The server is a single Node.js file, zero npm dependencies for core
node server.js
# Listening on port 3077

Then point the widget at your server:

<script src="powforge-captcha.min.js"
        data-target="#captcha"
        data-server="https://your-server.com:3077">
</script>

Server endpoints:

Endpoint Method Description
/api/challenge GET Get a new PoW challenge
/api/verify POST Submit solution, get token
/api/token/verify POST Server-side token validation

Difficulty Guide

Bits Expected Hashes Avg Time (desktop) Use Case
10 ~1,024 <1s Low friction, comment forms
14 ~16,384 ~1s Standard forms
16 ~65,536 ~4s Default. Good balance
18 ~262,144 ~15s High-value actions
20 ~1,048,576 ~60s Rate limiting, account creation

License

MIT