npm package: @powforge/captcha
Description: Self-hosted proof-of-work CAPTCHA. Privacy-first reCAPTCHA alternative with zero tracking.
Latest version: 0.1.0

# @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

```bash
npm install @powforge/captcha
```

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

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

### 2. Add the widget

```html
<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

```js
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)

```html
<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

```js
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`:

```js
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

```js
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:

```bash
# 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:

```html
<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