With Bun transitioning from Zig to Rust, why aren't we just using Rust directly on the web?
And so I did.
Currently in experimental phase, I deployed an Altcha challenge/verify server built on Rust & Actix. It runs as a multi-instance binary backed by an Nginx reverse proxy, with each instance binding to its own internal port, and all communicate via Redis. I could freely set how many instances were running from a few commands, which I later simplified into a script. Nginx load-balances everything and adds several layers of abuse protection, including an additional rate limiter built into the server itself.
API Endpoints
1. Configuration
Returns the server configuration, including all supported algorithms.
GET https://cerberus.hnawc.com/config
Response:
{ "algorithms": [ "ARGON2ID", "SCRYPT", "PBKDF2/SHA-256", "PBKDF2/SHA-384", "PBKDF2/SHA-512" ], "maxDifficulty": 0, "pow": "v2", "server": "cerberus.hnawc.com" }
2. Challenge
Returns a signed proof-of-work challenge. The client's widget uses this to generate a solution.
GET https://cerberus.hnawc.com/challenge
Query Parameter:
algo OR algorithm
Possible values are ARGON2ID, SCRYPT, PBKDF2/SHA-256, PBKDF2/SHA-384, and PBKDF2/SHA-512 with all case-insensitive and support common shorthand. The default algorithm is Argon2id as it is the current strongest one to fight bots, but that may change in the future as technology develops.
GET https://cerberus.hnawc.com/challenge?algo=argon2id
GET https://cerberus.hnawc.com/challenge?algo=scrypt
GET https://cerberus.hnawc.com/challenge?algo=PBKDF2/SHA-256
GET https://cerberus.hnawc.com/challenge?algo=SHA-384
GET https://cerberus.hnawc.com/challenge?algo=sha512
Response:
{ "parameters": { "algorithm": "ARGON2ID", "cost": 1, "expiresAt": 1781181120, "keyLength": 32, "keyPrefix": "476d20ac5f2a5361870125da64c75f65", "keySignature": "22f9787b3db56caa4cca8f2ac19d2bce09ef2942caa78c0272e766cbee0db046", "memoryCost": 131072, "nonce": "f45992822a154214d5e53193451aac07", "parallelism": 1, "salt": "58beee892019246b9c7d45d3f6b2520c" }, "signature": "420c55a9918a77df2b133e5b12cacf2ec4075ceb74cfe691784302e9587188fc" }
3. Verify
Submits a completed proof-of-work solution. The client's widget generates this after the challenge is solved.
POST https://cerberus.hnawc.com/verify
Example:
curl -X POST https://cerberus.hnawc.com/verify \ -H "Content-Type: application/json" \ -d ' { "challenge": { "parameters": { "algorithm": "ARGON2ID", "cost": 1, "expiresAt": 1781181935, "keyLength": 32, "keyPrefix": "8f58a823e137a04ebde45c7ec0a98bb1", "keySignature": "e32d295c093e2174d4ffd617de6d865f445b1bda95b89d2326aefc941504c048", "memoryCost": 131072, "nonce": "c7085f021f0b05342619c0be1d530dfa", "parallelism": 1, "salt": "16d9426feb30b0c969c2f8a7210dfefa" }, "signature": "e22e447110e4f8e1ee43ef36a86eb5c640afec149454a991475fadedd49a7d2f" }, "solution": { "counter": 12, "derivedKey": "8f58a823e137a04ebde45c7ec0a98bb1f5c1f8c5d8ed79017dce5b8a945faebc", "time": 2167.6 } } '
Response (success):
{"success": true}
Response (failure):
{"success": false, "error": "expired"}
Possible error messages:
malformed_json= Payload body couldn't be parsed.expired= The challenge'sexpiresAtis in the past.replay= Identical solved nonce exists, indicating a replay-attack.invalid_payload= Solution doesn't validate or payload was tampered.
How to Use
Use the new Altcha Widget v3 that supports POW v2 to enable Argon2id algorithm.
Example:
<head> <!-- Loads Altcha --> <script async defer src="https://cdn.jsdelivr.net/npm/altcha@3.0.11/dist/main/altcha.min.js" type="module"></script> </head> <body> <!-- Render widget & fetch the challenge --> <altcha-widget challenge="https://cerberus.hnawc.com/challenge" workers="1"></altcha-widget> <!-- Loads Argon2id to solve the challenge --> <script> window.addEventListener('load', async () => { const blob = await fetch('https://cdn.jsdelivr.net/npm/altcha@3.0.11/dist/workers/argon2id.js').then(r => r.blob()); $altcha.algorithms.set('ARGON2ID', () => new Worker(URL.createObjectURL(blob))); }); </script> </body>
Demo:
Once solved and the user submits the form, the payload is ready to be verified to /verify via a POST request from your end. If {"success": true}, process the form.
Practical Usage:
Altcha invisibly protects my own contact page. It auto-loads the challenge, silently solves it, then immediately submits to /verify and fetches my email address. Altcha Widget v3's auto=onload and display="invisible" make this frictionless setup possible without needing user interaction.