captcha-sdk 0.0.1

Unified server-side CAPTCHA verification for Rust
Documentation
# Effective capabilities

> **TARGET architecture — implementation pending.** The matrix is an initial
> design input, not a claim about implemented adapters. Verify every cell from
> official provider documentation during implementation.

## Model

```rust,ignore
#[non_exhaustive]
pub enum CapabilitySupport {
    Unsupported,
    Supported,
    Conditional(CapabilityCondition),
}

#[non_exhaustive]
pub struct Capabilities { /* private fields */ }

impl Capabilities {
    pub fn score(&self) -> &CapabilitySupport;
    pub fn action(&self) -> &CapabilitySupport;
    pub fn hostname(&self) -> &CapabilitySupport;
    pub fn challenge_time(&self) -> &CapabilitySupport;
    pub fn remote_ip(&self) -> &CapabilitySupport;
    pub fn expected_site_key(&self) -> &CapabilitySupport;
    pub fn idempotency(&self) -> &CapabilitySupport;
    pub fn local_verification(&self) -> &CapabilitySupport;
    pub fn supports_score(&self) -> bool;
    pub fn support(&self, capability: Capability) -> &CapabilitySupport;
}
```

`CapabilityCondition` is typed and inspectable, not free-form prose. It can
represent conditions such as an Enterprise subscription, a key type, or remote
IP submission. The normal discoverable API is grouped:
`captcha.capabilities().score()` returns authoritative typed support, while
`captcha.capabilities().supports_score()` is a boolean convenience. The latter
includes `Conditional` and means only “not `Unsupported`”; it does not promise
that every accepted verdict contains a score. `support(Capability::Score)` is an
advanced dynamic query, not the primary API.

Capabilities are effective for one configured `Captcha` instance. Product,
account tier, key type, and configuration can change them, so a global provider
constant is insufficient. `captcha.capabilities()` is authoritative after the
provider or advanced `CaptchaBuilder` has validated construction.

## Compatibility and missing evidence

`VerificationPolicy` builds independently with `.build()`. Provider-policy
compatibility is checked by the provider builder or advanced
`CaptchaBuilder::build()`. If policy requires an unsupported capability,
construction returns `CaptchaError` wrapping
`ConfigurationError::UnsupportedCapability { provider, capability }`.
Conditional support requires its condition to be satisfied by the configured
instance.

Support is not a guarantee that a field appears on every response. When an
accepted verdict omits evidence that a compatible policy requires, assessment
denies with `PolicyFailure::MissingEvidence(Capability)`. If a score is present
but below the configured threshold, the failure is `ScoreTooLow` instead.

`local_verification` means the adapter can completely validate a token during
`verify` without a provider network call. Cached configuration or local input
validation does not qualify. An adapter may be remote, local, or hybrid; a
hybrid adapter reports capabilities for the configured verification path rather
than claiming local support merely because some product variant can verify
locally.

`idempotency` reports an official mechanism the adapter can use to make a replay
safe; it does not expose an application-supplied key and does not guarantee a
retry will occur. `RetryPolicy::Safe` still requires the adapter to preserve one
internal per-attempt key and obey the same total timeout. Without idempotency,
retry is allowed only when the adapter proves `TokenState::NotSent`. See
[Attempts and safe retries](attempts-and-retries.md).

## Target capability planning matrix

Legend: **Yes** = supported by the product response/protocol; **Conditional** =
depends on product configuration, key type, tier, request input, or selected
verification path; **No** = not part of the identified product contract;
**TBD** = deliberately undecided until an official protocol reference is
reviewed. These tables guide implementation and every cell must be confirmed
against the linked official sources.

### First-wave product decisions

| Configured product | Score | Action | Hostname | Challenge time | Remote IP input | Idempotency | Local verification |
| --- | --- | --- | --- | --- | --- | --- | --- |
| reCAPTCHA v2 | No | No | Yes | Yes | Yes, optional | No | No |
| reCAPTCHA v3 | Yes | Yes | Yes | Yes | Yes, optional | No | No |
| reCAPTCHA Enterprise | Conditional | Conditional by key type | Conditional | Conditional | Conditional | No | No |
| hCaptcha | No | No | Yes | Yes | Yes, recommended | No | No |
| hCaptcha Enterprise | Conditional; remote IP enables scoring | No | Yes | Yes | Yes, recommended | No | No |
| Cloudflare Turnstile | No | Yes | Yes | Yes | Yes, optional | Yes | No |

`expected_site_key` and Enterprise-only evidence require adapter-specific
confirmation and are intentionally not generalized beyond verified protocol
behavior.

Sources: [reCAPTCHA verification](https://developers.google.com/recaptcha/docs/verify),
[reCAPTCHA v3](https://developers.google.com/recaptcha/docs/v3),
[reCAPTCHA Enterprise assessments](https://cloud.google.com/recaptcha/docs/create-assessment-website),
[hCaptcha](https://docs.hcaptcha.com/),
[hCaptcha Enterprise](https://docs.hcaptcha.com/ent_overview/), and
[Turnstile server-side validation](https://developers.cloudflare.com/turnstile/get-started/server-side-validation/).

### Exploratory second-wave inventory

These rows recognize products already explored by the unpublished scaffold.
They are not compatibility promises or finalized adapter splits. `TBD` is
preferred over inferring a capability from marketing material or an example.

| Candidate configured product/path | Score | Action | Hostname | Challenge time | Remote IP input | Idempotency | Local verification |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Friendly Captcha v2 | Conditional (Risk Intelligence) | TBD | No generic claim; protocol returns origin | Yes | TBD | TBD | No for Siteverify path |
| CaptchaFox | TBD | TBD | TBD | TBD | TBD | TBD | TBD |
| MTCaptcha CheckToken | TBD | Yes | Yes | Yes | TBD | TBD | No |
| MTCaptcha direct decrypt/verify | TBD | Yes | Yes | Yes | TBD | TBD | Yes |
| Prosopo Procaptcha | Conditional by tier | TBD | TBD | TBD | Conditional by tier | TBD | TBD |
| ALTCHA custom proof-of-work | TBD | TBD | TBD | TBD | TBD | TBD | Yes |
| ALTCHA Sentinel signature (local library/HMAC) | TBD | TBD | TBD | TBD | TBD | TBD | Yes |
| ALTCHA Sentinel verification API | TBD | TBD | TBD | TBD | TBD | TBD | No |

Friendly Captcha v2 Siteverify returns a challenge timestamp and an `origin`;
`origin` must remain distinct typed evidence unless official semantics justify
normalizing it as hostname. The protocol accepts a sitekey input, which informs
the separate `expected_site_key` capability. Risk Intelligence is conditional.
See [Friendly Captcha v2 Siteverify](https://developer.friendlycaptcha.com/docs/v2/api/siteverify).

MTCaptcha documents remote CheckToken validation with action, hostname, and
timestamps, and separately documents direct decrypt/verify. See
[MTCaptcha token validation](https://docs.mtcaptcha.com/dev-guide-validate-token).

Prosopo scoring and IP input depend on service tier. See
[Prosopo server-side verification](https://docs.prosopo.io/en/basics/server-side-verification/).

ALTCHA custom proof-of-work can be verified locally. Sentinel also has two
distinct backend paths: local library/HMAC verification of the Sentinel server
signature, and an HTTP verification API. A configured adapter must model the
selected path (or report it precisely as hybrid) rather than treating Sentinel
as remote-only. See [ALTCHA server verification](https://altcha.org/docs/v2/server-verification/)
and [proof-of-work CAPTCHA](https://altcha.org/docs/v2/proof-of-work-captcha/).

CaptchaFox remains all-TBD until an official protocol reference is reviewed;
the currently available [Next.js integration guide](https://captchafox.com/learn/how-to-protect-a-nextjs-application-against-bots-with-captchafox/)
and [Risk Intelligence overview](https://captchafox.com/learn/risk-intelligence-apis)
are inventory sources, not sufficient evidence for detailed capability cells.