Bolrach Guard API

Make automated abuse expensive. Every attempt costs the caller real work before your server sees it, and the token it produces is single-use and bound to one action.

Quick start

Base URL: https://api.bolrach.com/v1/guard

NODE
import { BolrachGuard } from '@bolrach/guard';

const guard = new BolrachGuard({ apiKey: process.env.BOLRACH_GUARD_KEY });

// On your signup route: the page sent a token with the form.
// Verifying SPENDS the token: it is single-use and bound to this action.
const result = await guard.verifyToken({ token: req.body.guard_token, action: 'signup' });
if (!result.valid) {
  // reason distinguishes 'expired' (ask again) from 'replayed' (treat as hostile).
  return res.status(400).json({ error: 'Could not verify that request.', reason: result.reason });
}
PYTHON
import os
from bolrach_guard import BolrachGuard

guard = BolrachGuard(api_key=os.environ["BOLRACH_GUARD_KEY"])

# Verifying SPENDS the token: it is single-use and bound to this action.
result = guard.verify_token({"token": form["guard_token"], "action": "signup"})
if not result["valid"]:
    # reason distinguishes "expired" (ask again) from "replayed" (treat as hostile).
    abort(400, "Could not verify that request: " + result["reason"])

Authentication

Verification uses your server key. Assessment uses a public site key, which is safe to ship in a page. Send it as a bearer token:

curl https://api.bolrach.com/v1/guard/... \
  -H "authorization: Bearer $YOUR_KEY"
A server key belongs on a server. Anything shipped to a browser is public the moment it loads, so keys never go in front-end code. Where a call genuinely has to happen in a page, it uses a separate public key that can do only that one thing.

SDKs

Official clients for Node and Python. Both retry on 429 and 5xx with exponential backoff, obey Retry-After, accept idempotency keys on writes, and raise a typed BolrachGuardError carrying status, code, message and requestId, so you branch on the failure instead of parsing a string.

NODE
npm install @bolrach/guard
PYTHON
pip install bolrach-guard

Every endpoint below lists its SDK method name. Anything not yet wrapped is still reachable without waiting for a release:

await client.request('GET', '/some/new/endpoint', { query: { days: 7 } });   // node
client.request('GET', '/some/new/endpoint', query={'days': 7})               # python

Errors and retries

Errors are JSON: {"error": {"code": "...", "message": "..."}}. The HTTP status carries the category, the code carries the specific reason.

STATUSCODEMEANING
400bad_requestThe body or a query parameter was missing or malformed. The message names the field.
401unauthorizedNo key, or a key this API does not recognise.
403forbiddenA valid key without the scope this call needs, or a key not bound to the resource.
404not_foundNo such resource, or one that belongs to somebody else. The two are deliberately indistinguishable.
409conflictThe same idempotency key was reused with a different body.
429rate_limitedToo many calls. Retry after the seconds in the Retry-After header, the SDKs already do.
5xxserver_errorSomething failed on our side. Safe to retry; the SDKs retry twice with backoff.
Retry safely. Every write accepts an Idempotency-Key header. Reusing a key returns the original result rather than applying the change twice, so a timeout you never saw the answer to is safe to repeat.

Endpoints

POST /v1/guard/tokens/verify verifyToken() · verify_token()

Verify a token your page produced.

Body: token, action. A token is single-use: verifying it spends it.

NODE
await client.verifyToken({ /* body */ });
PYTHON
client.verify_token({...})
POST /v1/guard/assessments assess() · assess()

Ask Guard to judge an attempt. Authenticates with the public site key, not the server key.

Authenticates with the public site key, not the server key, this call is made from a page.

NODE
await client.assess({ /* body */ });
PYTHON
client.assess({...})
POST /v1/guard/challenges challenge() · challenge()

Mint a proof-of-work challenge directly. The browser SDK does this for you.

NODE
await client.challenge({ /* body */ });
PYTHON
client.challenge({...})
POST /v1/guard/challenges/complete completeChallenge() · complete_challenge()

Submit a solved challenge and receive a spendable token.

NODE
await client.completeChallenge({ /* body */ });
PYTHON
client.complete_challenge({...})