How the proof of work works

Updated

  1. Challenge. Bot403 sends the browser a random salt and the SHA-256 hash of the salt plus a secret number between 0 and N.
  2. Solve. The browser tries 0, 1, 2, … until the hash matches. There is no shortcut, so it takes about N/2 tries.
  3. Check. Your server sends the answer to Bot403, which checks one hash and one signature.
browser      ── 1. get a challenge ──▶  Bot403
browser      ── 2. solve it, about 0.1 s
your server  ── 3. verify the token ──▶  Bot403  ──▶  ok, or a reason

What a challenge holds

Fetch one to see it:

curl "https://bot403.com/api/v1/challenge?pk=pk_live_YOUR_PUBLIC_KEY"
salt      = random 12 bytes + "?d=medium&expires=…&o=shop.example.com&pk=pk_live_…&"
challenge = SHA-256(salt + number)          number ∈ [0, maxnumber]
signature = HMAC-SHA256(site secret, challenge)

The salt carries the public key, the difficulty, the origin that asked and the expiry time. All of it is inside the hash, so a change to any of it makes the signature fail. Bot403 keeps a separate signing secret per site; the secret key you use in the API is a different value and is stored as a hash.

Difficulty

LevelmaxnumberHashes on averageLaptopOld phone
low20,00010,0000.02 s0.2 s
medium (default)100,00050,0000.1 s1 s
high400,000200,0000.4 s4 s
max (automated browsers only)1,500,000750,0001.5 s15 s

Each site has one fixed level. New sites start at medium. On Free the level is always medium; Starter and Pro let you pick low, medium or high in the site settings. Max is not a setting: it is used only when the automated-browser signals raise one challenge.

The level never depends on the visitor's IP address. Bot403 never stores or logs visitor IP addresses. The rate limits read the address for that request only.

Timing numbers are from one 2021 laptop and one 2017 phone; your visitors' devices vary.

What it does not do

A proof of work makes bulk submission expensive; it does not make it impossible. A bot farm with 1,000 CPUs can still post 1,000 forms a second at medium difficulty. For that traffic, add rate limits on your own server and look at the per-origin numbers in the dashboard. This is true of every invisible CAPTCHA: without a puzzle for the visitor, no product can prove that a person is there. reCAPTCHA v3 and Turnstile also only make abuse harder; they do not stop it.

Replay and expiry

Every accepted token's signature is stored until the token expires. A second verify call with the same token answers replay. Expired tokens answer expired, even when the puzzle was solved. The default expiry is 10 minutes; a site can set 1 to 60 minutes.

The open-source core

The protocol is Altcha v1 (MIT). The server uses github.com/altcha-org/altcha-lib-go. The browser script is Bot403's own 6 KB file with a SHA-256 solver that runs in a Web Worker; its output is a standard Altcha payload, so the Altcha widget is a drop-in alternative.