React Component
The @cernosh/react package exports a default widget for the full browser-side flow:
- challenge fetch
- deterministic maze rendering
- pointer and keyboard event capture
- proof-of-work mining
- ephemeral P-256 key generation
- challenge signing
- optional probe arming and Stroop overlay rendering
- optional WebAuthn authentication when the server requires or prefers it
- submission to your verification API
Installation
npm install @cernosh/reactPeer dependencies: react >= 18, react-dom >= 18.
Basic usage
import { Cerno } from '@cernosh/react'
function ProtectedForm() { const handleVerify = (token: string) => { fetch('/api/submit', { method: 'POST', body: JSON.stringify({ cerno_token: token }), }) }
return ( <Cerno siteKey="your-site-key" sessionId={session.id} stableId={user.id} apiUrl="/api/captcha" onVerify={handleVerify} /> )}Props
| Prop | Type | Required | Description |
|---|---|---|---|
siteKey | string | Yes | Site identifier sent with challenge creation and verification |
sessionId | string | Yes | Session identifier bound into the issued verification token |
stableId | string | No | App-level stable identifier used for WebAuthn and reputation |
onVerify | (token: string) => void | Yes | Fires after successful verification |
onError | (error: Error) => void | No | Receives network or verification flow errors |
onExpire | () => void | No | Fires when the current challenge expires and is replaced |
theme | ’light’ | ‘dark’ | No | Visual theme for the widget |
size | ’normal’ | ‘compact’ | No | Controls the rendered cell size and layout density |
apiUrl | string | No | Base URL prefix used for the worker-style routes |
Lifecycle
- The component requests a challenge from
{apiUrl}/challenge. - It regenerates the maze locally from the issued seed.
- It starts proof-of-work mining immediately, using a Worker when available and a main-thread fallback otherwise.
- It generates an ephemeral P-256 keypair and signs
challenge_id:site_key:expires_atbefore submit. - It captures raw interaction events and sends them as
events. - If the issued challenge requires probes, it calls
{apiUrl}/probe/arm, rendersStroopOverlay, then exchanges the result for acompletion_tokenvia{apiUrl}/probe/complete. - If the issued challenge includes
webauthn_request_options, it callsnavigator.credentials.get()automatically and includes the assertion in the final payload. - It submits the full payload to
{apiUrl}/verify. - On success, it calls
onVerify(token).
Exports besides Cerno
import { MazeCanvas, StroopOverlay, isWebAuthnAvailable, requestWebAuthnAuthentication, requestWebAuthnRegistration,} from '@cernosh/react'Use these when you want a custom integration instead of the stock widget.
Accessibility and behavior details
- Keyboard events are supported by the internal collectors.
- The widget auto-refreshes expired challenges.
- Reduced-motion is negotiated during challenge issuance. The client does not silently skip a required probe.
- Friendly error messages are mapped from server error codes before being shown in the component UI.
- The stock widget automatically performs WebAuthn authentication when the challenge includes
webauthn_request_options.
What the stock widget still leaves to you
Credential registration is still an app flow, not a widget flow. You must call
/webauthn/register/options and /webauthn/register/verify yourself before the widget can use a
credential during challenge verification.