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.
remoteIpis reported as the visitoripin 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
remoteIpin 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 withremoteIp.
The response contains:
verified— boolean PoW result. Only PoW v2 payloads are accepted; a legacy v1 payload returnsreason: "unsupported_pow_version".powVersion,algorithm,expired,invalidSignature,invalidSolution, andverificationMs— 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
| Mode | Behaviour |
|---|---|
| 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.