CAPTCHA Widget

A drop-in proof-of-work CAPTCHA delivered as a custom element. No external iframes, no user puzzles — verification runs invisibly in the browser.

Prerequisites

  • The tracking script must be loaded on the page.
  • Your site must have at least one CAPTCHA config (created automatically with each new site).

Basic usage

Place the custom element anywhere inside a form:

<form method="post" action="/submit">
    <input name="email" type="email" required>
    <sokol-captcha></sokol-captcha>
    <button type="submit">Send</button>
</form>

The widget auto-discovers your Site ID from the tracking script, fetches a challenge from /api/v1/sentinel/challenge, solves the ALTCHA v3 PoW v2 challenge, and injects a hidden altcha input into the form on submit.

For invisible auth flows, use:

<sokol-captcha name="altcha" display="invisible" page-type="login"></sokol-captcha>

Submit buttons stay enabled. On submit, Sokol temporarily disables the submitter, completes the invisible verification, then resumes the original form submission.

Verifying server-side

On your backend, POST the submitted token plus the user-supplied fields to /api/v1/sentinel/verify with the CAPTCHA server secret in the X-Sokol-Server-Secret header. Never put this secret in browser code or a URL. The public API key used to request a challenge is intentionally rejected by the verification endpoint:

POST /api/v1/sentinel/verify
Content-Type: application/json
X-Sokol-Server-Secret: YOUR_SERVER_SECRET

{
    "payload": "<value of altcha from the form>",
    "fields": { "email": "user@example.com" },
    "remoteIp": "198.51.100.23"
}

Because this call comes from your server, Sokol does not score the address of the HTTP request. When the visitor requests a challenge, Sokol binds the visitor's IP address into it (encrypted and covered by the challenge signature) and scores that address on verification. If your backend knows the visitor's address (for example from your own trusted proxy headers), send it as the optional remoteIp field. An invalid remoteIp is rejected with 422.

  • When both addresses are known, both are checked against Sokol's intelligence (bad actors, Tor, VPN/proxy, datacenter, internal threat indicators) and the riskier result is used. remoteIp is reported as the visitor ip in the signed verdict.
  • If the challenge was solved on one network and submitted from another (different /24 for IPv4 or /48 for IPv6, and a different ASN), the score rises by 15 with the reason Visitor IP changed.
  • If your server fetched the challenge itself, the bound address is your server's. Sokol ignores it and adds the reason Challenge requested by your server; send remoteIp in that setup.
  • Enable Require visitor IP on the security config to fail verification with reason: "visitor_ip_required" when no visitor address is known. The solution is not consumed, so you can retry with remoteIp.

The response contains:

  • verified — boolean PoW result. Only PoW v2 payloads are accepted; a legacy v1 payload returns reason: "unsupported_pow_version".
  • powVersion, algorithm, expired, invalidSignature, invalidSolution, and verificationMs — technical verification details.
  • score — risk score from 0 (clean) to 100 (very likely abuse).
  • reason — comma-separated list of triggered indicators (e.g. VPN/Proxy Detected).
  • verificationData + signature — the signed verdict you can store as audit evidence.

Each solved challenge can be verified once. Submitting the same payload again returns verified: false with reason: "replayed", so verify exactly once per form submission and act on that result. If Sokol cannot record the single use (only possible when its primary database is unavailable) it fails closed with reason: "replay_protection_unavailable"; treat it like any other failed verification and let the visitor retry. If you accept the signed verdict later in your own flow, check its time field and only accept it once.

Modes

ModeBehaviour
Fixed The configured difficulty level maps to a fixed PoW v2 profile. Low through Standard use PBKDF2/SHA-256; higher levels use Argon2id.
Smart The configured level is the baseline. Sokol raises it per request based on threat indicators and page type. See Smart Mode.

You can toggle modes from the site's Security tab in the console.

Sokol Edge challenge pages

The Sokol Edge enforcement page uses the same tracking bundle and <sokol-captcha> component, but supplies the local plugin challenge endpoint through challenge-url. This same-origin override prevents browser challenge creation from calling the central backend synchronously while retaining Sokol intelligence collection:

<script
    data-sokol-site="YOUR_SITE_ID"
    src="https://sokol-static.my-k.cloud/v1/sokol.iife.js"
    defer></script>
<sokol-captcha
    name="sokol"
    challenge-url="/.sokol/challenge?..."
    gate-submit="false"></sokol-captcha>

challenge-url is restricted to the current origin. The Edge Agent still chooses the proof-of-work profile from its synchronized policy and local risk context.