Documentation · v1

Invisible risk scoring

Novera helps your backend decide whether a request is likely human or automated without interrupting legitimate users with a puzzle.

How it works

The browser SDK collects bounded, privacy-conscious summaries and combines them with trusted edge, network, velocity, signed-device, and recent failure context. The browser receives only a random cap_… token. Your backend exchanges it using a secret key; only that authenticated response contains the score.

Trust rule: the browser is always attacker-controlled. Never accept a score, success flag, or recommended action from frontend code.
  1. Browser calls POST /api/v1/assessments with a public site key.
  2. Borderline live traffic automatically completes a short, single-use managed challenge.
  3. Novera stores the score and returns an opaque, short-lived token.
  4. Your frontend sends the token to your own backend with the user's request.
  5. Your backend calls POST /api/v1/siteverify with its secret key and applies policy.

Quick start

1. Execute in the browser

html example
<script src="https://captcha.95-111-249-143.sslip.io/captcha.js" async></script>
<script>
  await Captcha.ready();
  const token = await Captcha.execute("SITE_PUBLIC_KEY", {
    action: "login",
    timeout: 15000
  });

  // Send token with the form. It contains no readable score.
  await fetch("/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password, captcha_token: token })
  });
</script>

2. Verify in your backend

typescript example
const response = await fetch("https://captcha.95-111-249-143.sslip.io/v1/siteverify", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.NOVERA_SECRET_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    token: req.body.captcha_token,
    expected_action: "login",
    remote_ip: req.ip
  })
});

const result = await response.json();
if (!result.success || result.score < 0.5) {
  return res.status(403).json({ error: "Risk verification failed" });
}
Never expose your Secret Key in frontend code. CORS is not an authentication mechanism, and a secret embedded in JavaScript is public.

Browser SDK

The SDK is asynchronous, non-blocking, and safe to load globally. It exposes Captcha.ready(), Captcha.execute(), Captcha.reset(), and Captcha.configure(). A 15-second end-to-end timeout includes any managed challenge and prevents Novera availability from hanging the host application.

Managed escalation

When live traffic is borderline, the SDK transparently solves a bounded SHA-256 proof-of-work challenge and retries. The challenge expires in 45 seconds, accepts exactly one submission, and is bound to the original site key, origin, IP, signed device, and edge fingerprint.

Signals collected

Time before execution; counts of pointer, scroll, touch, focus, visibility, and keyboard-timing events; coarse browser/platform consistency; and software-renderer or automation-artifact booleans. Typed key values, form contents, passwords, DOM snapshots, and session replay are never collected.

NPM

typescript example
npm install @novera/browser

import { Captcha } from "@novera/browser";
const token = await Captcha.execute(siteKey, { action: "checkout" });

Server verification

Send the secret as a Bearer credential. expected_action is strongly recommended and prevents action substitution. Hostname and project binding are checked by Novera automatically.

cURL

bash example
curl -X POST https://captcha.95-111-249-143.sslip.io/v1/siteverify \
  -H "Authorization: Bearer $NOVERA_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"token":"cap_…","expected_action":"login"}'

Python

python example
from novera import Novera

client = Novera(secret_key=os.environ["NOVERA_SECRET_KEY"])
result = client.verify(token, expected_action="login")
if not result.success or result.score < 0.5:
    deny_request()

PHP

php example
$result = $novera->verify($token, ["expected_action" => "login"]);
if (!$result->success || $result->score < 0.5) {
    abort(403);
}

Go

go example
result, err := noveraClient.Verify(ctx, token, novera.VerifyOptions{
    ExpectedAction: "login",
})
if err != nil || !result.Success || result.Score < 0.5 { deny() }

Framework integrations

React / Next.js

Call Captcha.execute immediately before submitting. Send the token to a Route Handler or server action, then call Novera from that server boundary. Do not put NOVERA_SECRET_KEY in a NEXT_PUBLIC_ variable.

Express / Laravel / Django

Verify in middleware close to the protected handler. Check the declared action, then apply a route-specific threshold. For WordPress, verify via a server-side plugin hook before processing login, registration, or form submission.

Risk scores

0.00 means strongly automation-like; 1.00 means strongly human-like. Scores are probabilistic—not proof of identity.

RangeLevelManaged recommendation
0.80–1.00TrustedAllow
0.60–0.79LowAllow / monitor
0.40–0.59MediumMonitor / rate limit
0.20–0.39HighChallenge / MFA
0.00–0.19CriticalBlock

Actions & policies

Declare semantic actions such as login, signup, checkout, or password_reset. Managed mode recommends allow, monitor, rate_limit, challenge, mfa, or block. Your backend owns enforcement.

Token lifecycle

Tokens contain at least 256 bits of randomness, are stored as SHA-256 hashes, live for no more than 90 seconds in production, and are atomically consumed by successful verification. They are bound to project, site key, hostname, action, and the original browser IP. A token issued to one project cannot be verified with another project's secret.

Testing

Test keys are isolated from live projects and may request deterministic scenarios: always-human, always-bot, expired, and action-mismatch. Never route production traffic through a test project.

typescript example
await Captcha.execute(TEST_SITE_KEY, {
  action: "login",
  testScenario: "always-bot"
});

Use the protected production login to validate the live integration. Its backend rejects missing, invalid, expired, action-mismatched, and replayed tokens.

Error codes

CodeMeaning
TOKEN_EXPIREDThe short-lived token expired.
TOKEN_ALREADY_USEDA replay or duplicate verification was rejected.
ACTION_MISMATCHexpected_action does not match the issued token.
DOMAIN_NOT_ALLOWEDOrigin hostname is not configured.
SECRET_KEY_INVALIDThe server key is wrong, expired, or revoked.
RATE_LIMITEDA project, IP, or key limit was exceeded.

Security model

Novera assumes an attacker controls the browser, modifies SDK code, uses Burp Suite, automates with Selenium or Playwright, and sends custom HTTP requests. Public site keys are identifiers, not secrets. Secret keys are hashed at rest and never logged in full.

Burp-response tampering does not bypass Novera. The assessment endpoint never sends the score to the browser, and the customer backend accepts only the independently authenticated verification response.

The production edge overwrites forwarding headers and supplies server-observed TLS/HTTP context. Network scoring adds ASN, Tor/hosting classification, IP/subnet/device/connection velocity, signed device continuity, and decaying authentication failures. Direct assessment traffic that bypasses the trusted edge fails closed.

Tenant access is scoped by organization in centralized authorization helpers. Management writes require same-origin checks. Prepared database queries, bounded JSON payloads, exact CORS origin reflection, rate limits, and restrictive response headers provide layered protection.

Privacy

Data minimization is part of the architecture. Novera stores summarized features and high-level explanations, not raw interaction streams. IP addresses are partially masked for display and hashed per project for reputation. Default assessment retention is 30 days and is organization-configurable.

  • No keystroke values or typed content.
  • No passwords, form fields, or DOM snapshots.
  • No session replay.
  • No invasive cross-site identity graph.
  • Retention cleanup is designed as an idempotent scheduled job.

Failure modes

The browser SDK times out without blocking the host page. Your backend must choose availability behavior. Fail open is reasonable for low-impact actions such as contact forms; sensitive login, account recovery, and payment flows should use a stronger fallback, local rate limiting, or step-up authentication.

Rate limits

Assessment limits combine project, IP, IPv4 /24 or IPv6 /48 subnet, signed-device, and edge-fingerprint windows. Authentication failures decay over one hour, and successful login establishes 90-day device trust. Verification limits apply per secret key. Responses include standard RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers; exceeded limits return HTTP 429.

Key rotation

Rotate from Project → Keys. New secrets are displayed once and stored as hashes. During graceful rotation, old and new keys may remain active briefly. Revoke the old key after every backend instance has received the new value. Audit logs record the actor and prefix—never the full secret.

Webhooks

Events include risk.spike, attack.detected, usage.threshold, and secret.rotated. Deliveries use an event ID, timestamped HMAC signature, replay window, and exponential retries. The current console exposes lifecycle scaffolding while outbound delivery remains disabled until an email/webhook provider is configured.

FAQ

Can Novera stop every bot?

No. Bot detection is probabilistic and adversarial. Strong automation can proxy through real browsers, rotate residential IPs, and imitate interaction. Novera combines weak signals and recommends defense-in-depth.

Do shared NAT networks get banned?

No permanent ban is created from IP velocity alone. Network evidence is one probabilistic family combined with browser, behavior, action, and historical context.