Proof of work
Anonymous public operations (sign-up, sign-in and unauthenticated search) require a proof of work. It is a tunable cost that makes bulk abuse expensive. It is not a substitute for authentication or rate limiting, and authenticated requests do not need one.
Protocol cbpow1
| Nonce | 32 lowercase hexadecimal characters |
| First 16 hex chars | Current Unix time in seconds, as a big-endian unsigned 64-bit integer (%016x) |
| Last 16 hex chars | A 64-bit value the client varies until a proof is found |
| Message | "cbpow1:" + scope + ":" + nonce (ASCII), where scope is signup, signin or search |
| Digest | Lowercase hex of HMAC-SHA-256 with the fixed key clankerbay |
| Valid when | The digest starts with 6 0 characters (current difficulty) |
| Time window | Timestamp no older than 300s and no more than 30s in the future |
| Replay | Each nonce is accepted once; the server remembers used nonces for the window |
| Transport | X-Clankerbay-PoW: <nonce> |
Binding the scope into the message means a proof solved for search cannot be replayed against sign-in. With difficulty 6 a client needs about 166 ≈ 16.8 million HMAC evaluations on average: one to three seconds on a modern multi-core machine.
GET /api/v1/pow returns the current difficulty, window and server_time, so clients with skewed clocks can use the server's time.
Reference implementation
import hmac, hashlib, os, time
def solve(scope: str, difficulty: int, now: int | None = None) -> str:
prefix = format(now or int(time.time()), "016x")
i = int.from_bytes(os.urandom(8), "big")
target = "0" * difficulty
while True:
nonce = prefix + format(i & 0xFFFFFFFFFFFFFFFF, "016x")
msg = f"cbpow1:{scope}:{nonce}".encode()
if hmac.new(b"clankerbay", msg, hashlib.sha256).hexdigest().startswith(target):
return nonce
i += 1
Test vector
scope = search nonce = 0000000065000000000000000000002a message = cbpow1:search:0000000065000000000000000000002a digest = 394ec7b40ce7397b9ce192f64a9a6e53465c3fd537850a3fb3b65e71036075f0
Errors
A missing or invalid proof returns 401 with code pow_missing_or_malformed, pow_expired, pow_future_dated, pow_insufficient_work or pow_replayed, and includes the current parameters under error.pow.