> ## Documentation Index
> Fetch the complete documentation index at: https://ribaunt-e66481b6-mintlify-4e7c3afc.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Types

> Complete TypeScript type reference for Ribaunt: ChallengeToken, ChallengeSolution, RiskSignals, ReplayStore, VerifySolutionOptions, and more.

Ribaunt ships TypeScript types for all its public APIs. Import types from `ribaunt` for use in your server-side code.

## `ChallengeToken`

A signed JWT challenge token. This is the string value returned in the array from `createChallenge()` and the value you pass back to `verifySolution()`.

```ts theme={null}
type ChallengeToken = string; // A signed JWT challenge token
```

## `ChallengeSolution`

The proof-of-work solution produced by the browser solver (or by `solveChallenge()` in tests). The `nonce` is the value that, when hashed with the challenge string, produces a digest beginning with the required number of leading zero hex digits. The hash is SHA-256 or Argon2id depending on the token's algorithm.

```ts theme={null}
interface ChallengeSolution {
  nonce: string; // The nonce that satisfies the PoW condition
  hash: string;  // The hash (hex) produced by nonce
}
```

## `PowAlgorithm` and `ArgonProfile`

`PowAlgorithm` selects the proof-of-work algorithm for `createChallenge()` and `selectWorkload()`. The default is `'sha256'`; `'argon2id'` is a memory-hard opt-in — see [Argon2id opt-in](/api/create-challenge#argon2id-opt-in).

`ArgonProfile` abstracts the raw Argon2id parameters so you never pass memory sizes yourself. It is only valid when the algorithm is `'argon2id'`. Both profiles currently resolve to the same conservative parameters (`m: 8192`, `t: 1`, `p: 1`, `hashLen: 32`). Because each token embeds its own parameters and a construction version, choosing a tier now is safe: retuning in a future release cannot break in-flight tokens.

```ts theme={null}
type PowAlgorithm = 'sha256' | 'argon2id';

type ArgonProfile = 'mobile' | 'standard';
```

`HARD_MAX` is the library-enforced upper bound on Argon2id parameters. Tokens carrying parameters above these values are rejected with `invalid-token`, so a tampered token cannot force a browser to allocate unbounded memory.

```ts theme={null}
const HARD_MAX = { m: 32768, t: 3, p: 1, hashLen: 32 } as const;
```

## `ChallengeOptions`

The options object accepted by `createChallenge()`.

```ts theme={null}
interface ChallengeOptions {
  difficulty?: number | 'auto';
  amount?: number;
  ttlSeconds?: number;
  context?: string;
  workload?: Pick<Workload, 'difficulty' | 'amount'>;

  // Only used when difficulty === 'auto':
  targetDurationMs?: number;   // default 750
  riskScore?: number;          // 0–100, default 50
  calibration?: ClientCalibration;
  minDifficulty?: number;      // default 3
  maxDifficulty?: number;      // default 6
  minAmount?: number;          // default 1
  maxAmount?: number;          // default 8

  // Algorithm selection:
  algorithm?: PowAlgorithm;    // default 'sha256'
  argonProfile?: ArgonProfile; // only with algorithm: 'argon2id'

  // Extension hooks:
  rateLimiter?: RateLimiter;
  onEvent?: (event: RibauntEvent) => void;
}
```

With `algorithm: 'argon2id'`, `minDifficulty` and `maxDifficulty` default to `1` and `2`, and difficulty caps at `8` instead of `64`.

## `ClientCalibration`

Reported benchmark from the client used as a raise-only signal in `"auto"` mode.

```ts theme={null}
interface ClientCalibration {
  iterations: number;
  durationMs: number;
}
```

## `Workload` and `AdaptiveWorkloadOptions`

`selectWorkload()` returns a `Workload` object and accepts an optional adaptive configuration.

```ts theme={null}
interface AdaptiveWorkloadOptions extends WorkloadBounds {
  riskScore?: number;
  targetDurationMs?: number;
  calibration?: ClientCalibration;
  algorithm?: PowAlgorithm;    // default 'sha256'
  argonProfile?: ArgonProfile; // only with algorithm: 'argon2id'
}

interface Workload {
  difficulty: number;
  amount: number;
  estimatedAttempts: number;
  algorithm: PowAlgorithm;
  argon?: { m: number; t: number; p: number; hashLen: number }; // present for 'argon2id'
}
```

## `RiskSignals`

Caller-supplied signals passed to [`assess()`](/api/assess). All fields are optional and treated as untrusted. The index signature lets custom scorers consume application-specific keys; the default scorer ignores unknown keys.

```ts theme={null}
interface RiskSignals {
  ip?: string;
  userAgent?: string;
  accountAgeSeconds?: number;
  requestVelocity?: number;
  [key: string]: unknown;
}
```

## `RiskScorer`

The interface you implement to replace the default risk heuristic in `assess()`. `score()` may be async so you can call a remote model or service. It must return a finite number from 0 to 100; otherwise, `assess()` rejects.

```ts theme={null}
interface RiskScorer {
  score(signals: RiskSignals): number | Promise<number>;
}
```

## `RiskThresholds` and `DEFAULT_RISK_THRESHOLDS`

Decision boundaries for `assess()`. Validation requires `0 <= challenge < block <= 100`; invalid thresholds throw instead of being silently repaired. `DEFAULT_RISK_THRESHOLDS` is a frozen object exported from `ribaunt`.

```ts theme={null}
interface RiskThresholds {
  challenge: number; // risk < challenge            -> allow
  block: number;     // challenge <= risk < block   -> challenge
}                    // risk >= block               -> block

const DEFAULT_RISK_THRESHOLDS: RiskThresholds = { challenge: 40, block: 80 };
```

## `AssessOptions` and `AssessWorkloadOptions`

The options object accepted by `assess()`. `workload` bounds are only used when the resulting action is `challenge`, but they are validated on every call. `workload` also accepts `algorithm` and `argonProfile`, which thread through to the generated `Workload`.

```ts theme={null}
interface AssessWorkloadOptions extends WorkloadBounds {
  targetDurationMs?: number;
  calibration?: ClientCalibration;
}

interface AssessOptions {
  signals: RiskSignals;
  scorer?: RiskScorer;
  thresholds?: RiskThresholds;
  workload?: AssessWorkloadOptions;
}
```

## `RiskAssessment`

The result returned by `assess()`. `risk` is a bounded heuristic score, not a fraud probability. `workload` is present only when `action` is `'challenge'` and is produced by the same `selectWorkload()` engine used for adaptive difficulty.

```ts theme={null}
interface RiskAssessment {
  risk: number;                            // 0–100, finite
  action: 'allow' | 'challenge' | 'block';
  workload?: Workload;
}
```

## `ReplayStore`

The interface you implement to provide distributed replay prevention when `replayPrevention` is set to `'remote'`. You pass your implementation to `verifySolution()` via `options.replayStore`.

```ts theme={null}
interface ReplayStore {
  consume(jti: string, expiresAt: number): Promise<boolean>;
  consumeMany?(jtis: string[], expiresAt: number): Promise<boolean>;
}
```

`jti` is the JWT token ID uniquely identifying the challenge. `expiresAt` is a Unix timestamp in seconds indicating when the token expires. Return `true` to allow the submission (first use) and `false` to reject it (replay detected). Your implementation must be atomic — use a primitive such as Redis `SET NX EXAT` to avoid race conditions.

## `LocalReplayStore`

The built-in, process-local implementation of `ReplayStore` exported from `ribaunt`. It stores consumed token IDs in memory and automatically evicts entries once their TTL has passed. Use this when `replayPrevention` is `'local'` (the default) — it is used automatically in that mode. You can also instantiate it directly when you need a dedicated per-handler store.

```ts theme={null}
class LocalReplayStore implements ReplayStore {
  async consume(jti: string, expiresAt: number): Promise<boolean>;
  async consumeMany(jtis: string[], expiresAt: number): Promise<boolean>;
}
```

```ts theme={null}
import { LocalReplayStore } from 'ribaunt';

const store = new LocalReplayStore();

const result = await verifySolution(tokens, solutions, {
  replayPrevention: 'remote',
  replayStore: store,
});
```

`LocalReplayStore` is not suitable for multi-process or serverless deployments. For those environments, implement your own `ReplayStore` backed by a distributed atomic store such as Redis `SET NX EXAT`.

## `ReplayPreventionMode`

Controls how `verifySolution()` prevents a token from being submitted more than once.

```ts theme={null}
type ReplayPreventionMode = 'disabled' | 'local' | 'remote';
```

* **`'local'`** (default) — replay checks are process-local. Suitable for single-process deployments.
* **`'remote'`** — replay checks use your `ReplayStore`. Use this for serverless or horizontally scaled deployments.
* **`'disabled'`** — no replay checks. Tokens can be reused until they expire. Only use this if another layer handles replay prevention.

## `VerifySolutionOptions`

Optional configuration passed as the third argument to `verifySolution()`.

```ts theme={null}
interface VerifySolutionOptions {
  replayPrevention?: ReplayPreventionMode; // default: 'local'
  replayStore?: ReplayStore;               // required when replayPrevention is 'remote'
  context?: string;                        // optional challenge scope
  debug?: boolean;                         // default: true in development
  onWarning?: (warning: VerifyWarning) => void;
  rateLimiter?: RateLimiter;
  onEvent?: (event: RibauntEvent) => void;
}
```

## `RateLimiter`

The interface you implement to plug your own rate limiter into `createChallenge()` and `verifySolution()`. Ribaunt calls `check()` with the optional `context` value from the same call. Resolve `true` to allow, `false` to reject. Rejection surfaces as a thrown [`RateLimitedError`](#ratelimitederror).

```ts theme={null}
interface RateLimiter {
  check(key?: string): Promise<boolean>;
}
```

The `key` argument is whatever you passed as `context` to `createChallenge()` or `verifySolution()`. Remember that `context` also binds tokens cryptographically, so both calls must use the identical value. To bucket by request data such as the client IP, close over the request in your `check()` implementation instead of passing it as `context`. `check()` may be async, which is why `createChallenge()` returns a `Promise`.

## `RateLimitedError`

Thrown by `createChallenge()` or `verifySolution()` when a supplied `rateLimiter.check()` resolves `false`. Catch this to return a 429 response.

```ts theme={null}
class RateLimitedError extends Error {
  readonly code: 'rate-limited';
  readonly name: 'RateLimitedError';
}
```

```ts theme={null}
import { verifySolution, RateLimitedError } from 'ribaunt';

try {
  const result = await verifySolution(tokens, solutions, { rateLimiter });
  // ...
} catch (error) {
  if (error instanceof RateLimitedError) {
    return res.status(429).json({ error: error.message });
  }
  throw error;
}
```

## `RibauntEvent`

Discriminated union passed to the optional `onEvent` telemetry hook on both `createChallenge()` and `verifySolution()`. Use it to feed metrics or logs without wrapping Ribaunt yourself.

```ts theme={null}
type RibauntEvent =
  | { type: 'challenge-issued'; difficulty: number; amount: number; algorithm?: PowAlgorithm }
  | { type: 'verify-success' }
  | { type: 'verify-failure'; reason: VerifyFailureReason; message: string };
```

* `challenge-issued` — emitted by `createChallenge()` after tokens are signed.
* `verify-success` — emitted by `verifySolution()` when the submission passes all checks.
* `verify-failure` — emitted by `verifySolution()` when the submission is rejected. `reason` reuses the same [`VerifyFailureReason`](#verifyfailurereason-and-verifywarningreason) union as `VerifyWarning`, so you can share dashboards between the two hooks.

Exceptions thrown from an `onEvent` callback are caught and ignored, so telemetry can never break challenge issuance or verification.

## `VerifySolutionResult`

The structured result returned by `verifySolution()`.

```ts theme={null}
type VerifySolutionResult =
  | { valid: true }
  | { valid: false; reason: VerifyFailureReason; message: string };
```

## `VerifyWarning`

The structured warning object passed to the `onWarning` callback when `verifySolution()` encounters a problem. This allows you to capture telemetry without enabling console output.

```ts theme={null}
interface VerifyWarning {
  reason: VerifyWarningReason;
  message: string;
  error?: unknown;
}
```

## `VerifyFailureReason` and `VerifyWarningReason`

A string union describing the category of verification failure. You can use this to route warnings to different monitoring channels or metrics.

```ts theme={null}
type VerifyFailureReason =
  | 'invalid-token'
  | 'expired-token'
  | 'invalid-solution'
  | 'context-mismatch'
  | 'replay-detected'
  | 'replay-store-unavailable'
  | 'configuration-error';

type VerifyWarningReason = VerifyFailureReason;
```

`replay-store-unavailable` is reported when your `ReplayStore` throws (for example, Redis is unreachable). Verification fails closed with this reason instead of miscategorizing the outage as `invalid-token`, so you can alert on store health separately from bad submissions.

## `SolveChallengeOptions`

Optional guardrails passed to `solveChallenge()` and `solveChallengeAsync()` to prevent them from running indefinitely during tests.

```ts theme={null}
interface SolveChallengeOptions {
  maxIterations?: number; // hard cap on nonce attempts
  maxDurationMs?: number; // default: 30000 ms
}
```

## `WidgetState`

Imported from `ribaunt/widget`. Represents the current state of the CAPTCHA widget in the browser.

```ts theme={null}
type WidgetState = 'initial' | 'fetching' | 'solving' | 'verifying' | 'done' | 'error';
```

## `WidgetErrorCode`

Machine-readable error classification emitted on the widget's `error` event `detail.code`.

```ts theme={null}
type WidgetErrorCode =
  | 'timeout'
  | 'aborted'
  | 'challenge-fetch-failed'
  | 'invalid-challenge'
  | 'solve-failed'
  | 'verification-failed'
  | 'worker-unavailable'
  | 'unknown';
```

## `WidgetError`

Exported from `ribaunt/widget`. The error class the widget throws internally when a verification attempt fails. Every instance carries a machine-readable `code` (a [`WidgetErrorCode`](#widgeterrorcode)), so you can classify failures without parsing human-readable messages. The same code is surfaced on the `error` event's `detail.code`.

```ts theme={null}
class WidgetError extends Error {
  readonly code: WidgetErrorCode;
  readonly name: 'WidgetError';
}
```

## `RibauntWidgetHandle`

Imported from `ribaunt/widget-react`. This is the imperative handle exposed via a React `ref` attached to the `<RibauntWidget>` component. Use it to programmatically control the widget from your application code.

```ts theme={null}
interface RibauntWidgetHandle {
  reset(): void;
  getState(): WidgetState | '';
  startVerification(): void;
}
```
