Skip to main content
This guide walks you through building a complete Ribaunt CAPTCHA integration: two server endpoints that issue and verify proof-of-work challenges, plus a browser widget your users interact with. By the end you’ll have a working bot-protection layer you can drop in front of any form or API action.
1

Install Ribaunt

Add the ribaunt package to your project.
npm install ribaunt
2

Set your secret

Ribaunt signs every challenge token with a secret you control. Add RIBAUNT_SECRET to your server environment — a .env file, your hosting platform’s secrets manager, or however you manage server config.
RIBAUNT_SECRET="replace-with-a-long-random-secret"
Never expose RIBAUNT_SECRET to the browser. In Next.js, do not prefix it with NEXT_PUBLIC_ — that would embed the secret in your client bundle and allow anyone to forge valid challenge tokens.
3

Create server endpoints

You need two endpoints: one that issues challenges and one that verifies solutions. Here’s a complete Express example using the createChallenge and verifySolution functions from ribaunt.
import 'dotenv/config';
import express from 'express';
import { createChallenge, verifySolution } from 'ribaunt';

const app = express();
app.use(express.json());

// Issues signed JWT challenges to the browser widget
app.get('/api/captcha/challenge', (_req, res) => {
  const challenges = createChallenge(5, 4, 120);
  // createChallenge(difficulty, amount, ttlSeconds)
  res.json({ challenges });
});

// Verifies the nonces submitted by the browser widget
app.post('/api/captcha/verify', async (req, res) => {
  const { tokens, solutions } = req.body;
  const valid = await verifySolution(tokens, solutions);

  if (!valid) {
    return res.status(400).json({ success: false, error: 'Invalid CAPTCHA solution' });
  }

  return res.json({ success: true });
});

app.listen(3000);
createChallenge accepts three parameters:
ParameterDefaultDescription
difficulty5Leading zeros required in the SHA-256 hash. Higher values increase solve time.
amount4Number of challenge tokens to create.
ttlSeconds30Challenge token lifetime in seconds.
verifySolution returns a Promise<boolean>. It validates the JWT signature, checks expiry, confirms the nonce produces a hash with the required leading zeros, and blocks replay by default using process-local storage.
4

Add the widget to your frontend

Include the Ribaunt web component script and place the <ribaunt-widget> element wherever you need CAPTCHA protection. The widget fetches a challenge from your server, solves it in the background, then calls your verify endpoint automatically when auto-verify="true" is set.
<script type="module" src="/node_modules/ribaunt/dist/widget-browser.js"></script>

<ribaunt-widget
  challenge-endpoint="/api/captcha/challenge"
  verify-endpoint="/api/captcha/verify"
  auto-verify="true"
></ribaunt-widget>

<script>
  const widget = document.querySelector('ribaunt-widget');

  widget.addEventListener('verify', (e) => {
    console.log('Verified!', e.detail.solutions);
  });
</script>
If you’re using React, import the wrapper component instead of the web component directly. See React Integration for the full example.
The widget emits a verify event when the challenge is solved. Use it to enable your submit button or proceed with form submission. A corresponding error event fires if verification fails, giving you a chance to surface feedback to the user.

Next steps

Widget configuration

Explore all widget attributes — timeouts, theming, warning banners, and the disabled state.

React integration

Use the ribaunt/widget-react wrapper with full prop support in React and Next.js App Router.

Express server example

A production-ready Express server setup with replay protection and structured warning callbacks.