# HTTP transport boundary
> **TARGET architecture — implementation pending.** This is the authoritative
> contract for the advanced HTTP transport SPI. Concrete dependency and runtime
> choices remain implementation decisions.
## Normal and advanced construction
Normal users do not configure transport. A built-in provider builder installs a
secure default with connection reuse, TLS verification, and bounded I/O.
Advanced users may inject a transport for proxies, custom TLS, tracing, or
deterministic tests:
```rust,ignore
// Illustrative only: this concrete transport may be feature-provided, renamed,
// or remain internal depending on the implementation dependency decision.
let transport = ReqwestTransport::builder().proxy(proxy).build()?;
let captcha = Turnstile::builder(secret)
.transport(transport)
.build()?;
```
The example name `ReqwestTransport` does not commit the public API to reqwest.
Provider builders remain generic over, or erase, the small SDK transport SPI.
## Object-safe SPI
```rust,ignore
pub trait HttpTransport: Send + Sync + 'static {
fn send(&self, request: TransportRequest) -> TransportFuture<'_>;
}
pub type TransportFuture<'a> = Pin<Box<
dyn Future<Output = Result<TransportResponse, TransportError>> + Send + 'a
>>;
```
The exact alias syntax may be refined, but object-safe async behavior and the
SDK-owned boundary are normative. `TransportRequest`, `TransportResponse`, and
`TransportError` expose private fields with getters and builders only where
external construction is necessary. They never expose reqwest or another HTTP
client's public types.
`TransportRequest` contains the HTTP method, validated HTTPS URL, constrained
headers, bounded serialized body, and remaining timeout budget.
`TransportResponse` contains status, constrained headers, and a bounded body.
`TransportError` contains safe normalized failure information and a redacted
source where appropriate.
## One-send responsibility
One `send` call performs one HTTP exchange. Transport has no CAPTCHA semantics:
it does not evaluate policy, interpret provider responses, generate idempotency
keys, retry, or decide whether a token can be replayed. Those responsibilities
remain with the adapter and attempt orchestration described in
[Attempts and safe retries](attempts-and-retries.md).
A custom transport necessarily sees serialized credential and token bytes in
`TransportRequest`. It is a privileged trust boundary. The SDK can document and
constrain the interface but cannot prevent a malicious implementation from
copying or logging those bytes.
## Security constraints
- Production provider endpoints require HTTPS with certificate verification.
- Redirects are disabled so credentials cannot be forwarded to a new origin.
- Request and response bodies and header collections are bounded.
- The remaining total-operation timeout and cancellation propagate to I/O.
- Errors are normalized and redact URLs, headers, bodies, credentials, tokens,
and unsafe upstream messages.
- The default transport reuses connections and TLS sessions safely.
- Endpoint override remains dangerous, test-only, and feature-gated.
Dropping the returned future cancels owned I/O where the HTTP implementation
allows, but it does not prove that the request was not received. The adapter
classifies the resulting `TokenState` conservatively.