# How to Configure the HTTP Client and Custom TLS Settings in Forge

> Explore Forge's three-layer HTTP client architecture built on reqwest. Learn to configure HTTP client and custom TLS settings using code, TOML, or environment variables.

- Repository: [Forge Code/forgecode](https://github.com/antinomyhq/forgecode)
- Tags: how-to-guide
- Published: 2026-04-08

---

**Forge employs a three-layer architecture built on `reqwest` that separates HTTP configuration into domain models, configuration files, and infrastructure builders, enabling granular TLS control via code, TOML, or environment variables.**

Forge is an AI-powered coding assistant developed in the `antinomyhq/forgecode` repository. Its HTTP client configuration architecture allows developers to customize connection behavior, timeouts, and TLS settings through a type-safe domain model that bridges user configuration with the underlying `reqwest` client.

## HTTP Client Architecture Overview

Forge organizes HTTP client configuration into three distinct layers that separate data definitions from implementation details. This architecture ensures that TLS settings, timeouts, and connection pooling can be specified declaratively while the actual `reqwest::Client` instantiation remains encapsulated in the infrastructure layer.

### Domain Layer: HttpConfig

The foundational data structures reside in `forge_domain::HttpConfig`, which defines every tunable HTTP option including timeouts, connection pools, redirects, DNS settings, and TLS parameters. This pure data model contains fields for **TLS backend selection**, **version constraints**, and **certificate validation**.

Located in [`crates/forge_domain/src/http_config.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/http_config.rs), this struct includes `TlsVersion` and `TlsBackend` enums that map directly to `reqwest` types through conversion functions. The domain layer contains no networking logic, serving only as the canonical schema for HTTP behavior.

### Configuration Layer: ForgeConfig

The configuration crate aggregates user preferences through `forge_config::ForgeConfig`, which holds an optional `http: Option<HttpConfig>` field. This layer handles deserialization from TOML/YAML files and environment variables using the `FORGE_HTTP_*` prefix pattern.

In [`crates/forge_config/src/config.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_config/src/config.rs), the configuration parser merges file-based settings with environment overrides, producing a resolved `HttpConfig` that propagates to the infrastructure layer. This separation allows operators to configure TLS settings without modifying application code.

### Infrastructure Layer: ForgeHttpInfra

The concrete HTTP client materializes in [`crates/forge_infra/src/http.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_infra/src/http.rs) within `ForgeHttpInfra::new`. This function accepts a `ForgeConfig`, extracts the `HttpConfig` (falling back to defaults if absent), and constructs a `reqwest::Client` through the builder pattern.

Key translation steps include:
- Mapping `TlsVersion` to `reqwest::tls::Version` via `to_reqwest_tls`
- Selecting the TLS backend through `client.use_rustls_tls()` when `tls_backend == TlsBackend::Rustls`
- Loading custom root certificates from `root_cert_paths` using `Certificate::from_pem` or `from_der`
- Enabling `danger_accept_invalid_certs(true)` when the `accept_invalid_certs` flag is set

## Configuring Custom TLS Settings

Forge exposes comprehensive TLS customization through the `HttpConfig` struct, configurable via three interfaces: programmatic Rust construction, TOML configuration files, or environment variables.

### Available TLS Options

| Field | Type | Description | Environment Variable |
|-------|------|-------------|---------------------|
| `tls_backend` | `TlsBackend` | Selects the TLS implementation (`Default` for platform native, `Rustls` for pure Rust) | `FORGE_HTTP_TLS_BACKEND` |
| `min_tls_version` | `Option<TlsVersion>` | Minimum allowed protocol version (e.g., `1.2`) | `FORGE_HTTP_MIN_TLS_VERSION` |
| `max_tls_version` | `Option<TlsVersion>` | Maximum allowed protocol version (e.g., `1.3`) | `FORGE_HTTP_MAX_TLS_VERSION` |
| `accept_invalid_certs` | `bool` | **Dangerous** - bypasses certificate validation (testing only) | `FORGE_HTTP_ACCEPT_INVALID_CERTS` |
| `root_cert_paths` | `Option<Vec<PathBuf>>` | Additional PEM/DER files to trust beyond system store | `FORGE_HTTP_ROOT_CERT_PATHS` (comma-separated) |

When `ForgeHttpInfra` instantiates the client, these values translate directly to `reqwest::ClientBuilder` method calls:

```rust
if let Some(v) = http.min_tls_version {
    client = client.min_tls_version(to_reqwest_tls(v));
}
if let Some(v) = http.max_tls_version {
    client = client.max_tls_version(to_reqwest_tls(v));
}
match http.tls_backend {
    TlsBackend::Rustls => client = client.use_rustls_tls(),
    TlsBackend::Default => {} // platform default
}
if http.accept_invalid_certs {
    client = client.danger_accept_invalid_certs(true);
}
if let Some(paths) = http.root_cert_paths {
    for p in paths { /* read PEM/DER and add_root_certificate */ }
}

```

### Method 1: Programmatic Configuration

Construct `HttpConfig` directly in Rust for compile-time safety:

```rust
use forge_domain::{HttpConfig, TlsVersion, TlsBackend};

let http_cfg = HttpConfig {
    // Timeouts (seconds)
    connect_timeout: 20,
    read_timeout: 300,
    // TLS customization
    tls_backend: TlsBackend::Rustls,
    min_tls_version: Some(TlsVersion::V1_2),
    max_tls_version: Some(TlsVersion::V1_3),
    accept_invalid_certs: false,
    root_cert_paths: Some(vec![
        "/etc/ssl/custom-ca.pem".into(),
        "/home/user/mycert.der".into(),
    ]),
    ..HttpConfig::default()
};

```

### Method 2: TOML Configuration File

Define settings in a configuration file that `forge_config` loads at runtime:

```toml

# config.toml

[http]
connectTimeout = 20
readTimeout = 300
tlsBackend = "rustls"
minTlsVersion = "1.2"
maxTlsVersion = "1.3"
acceptInvalidCerts = false
rootCertPaths = "/etc/ssl/custom-ca.pem,/home/user/mycert.der"

```

Load the configuration using `forge_config::ForgeConfig::from_path("config.toml")`. The `http` section maps directly onto the `HttpConfig` struct fields.

### Method 3: Environment Variables

Override or define settings without touching configuration files:

```bash
export FORGE_HTTP_TLS_BACKEND=rustls
export FORGE_HTTP_MIN_TLS_VERSION=1.2
export FORGE_HTTP_MAX_TLS_VERSION=1.3
export FORGE_HTTP_ROOT_CERT_PATHS="/etc/ssl/custom-ca.pem,/home/user/mycert.der"
export FORGE_HTTP_ACCEPT_INVALID_CERTS=false

```

### Complete Application Example

Integrate the configuration into a running application:

```rust
use forge_infra::http::ForgeHttpInfra;
use forge_app::FileWriterInfra;
use std::sync::Arc;

async fn run(http_cfg: HttpConfig) -> anyhow::Result<()> {
    // Wrap the config in the top-level ForgeConfig
    let forge_cfg = forge_config::ForgeConfig {
        http: Some(http_cfg),
        ..Default::default()
    };

    // Provide a concrete FileWriterInfra implementation
    let file_writer: Arc<dyn FileWriterInfra> = Arc::new(forge_infra::fs_write::FsWriteInfra::default());

    // Build the HTTP infra with custom TLS settings
    let http = ForgeHttpInfra::new(forge_cfg, file_writer);

    // Execute request using configured client
    let url = reqwest::Url::parse("https://api.example.com/health")?;
    let resp = http.http_get(&url, None).await?;
    println!("Status: {}", resp.status());
    Ok(())
}

```

## Summary

- **Three-layer architecture**: Forge separates HTTP concerns into `HttpConfig` (domain), `ForgeConfig` (serialization), and `ForgeHttpInfra` (implementation) to maintain clean boundaries between data and networking logic.
- **Source locations**: Domain models live in [`crates/forge_domain/src/http_config.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_domain/src/http_config.rs), configuration aggregation in [`crates/forge_config/src/config.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_config/src/config.rs), and the `reqwest::Client` builder in [`crates/forge_infra/src/http.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_infra/src/http.rs).
- **TLS flexibility**: Choose between Rustls and native TLS backends, constrain protocol versions to TLS 1.2 or 1.3, mount custom certificate authorities, and control certificate validation.
- **Configuration interfaces**: Set TLS parameters via Rust code, TOML files, or `FORGE_HTTP_*` environment variables, with the infrastructure layer automatically applying them to the underlying `reqwest` client.

## Frequently Asked Questions

### What TLS backends does Forge support?

Forge supports two TLS backends configured through the `tls_backend` field in `HttpConfig`. Setting `TlsBackend::Rustls` forces the pure Rust Rustls implementation via `client.use_rustls_tls()`, while `TlsBackend::Default` uses the platform's native TLS library (typically OpenSSL on Linux, Secure Transport on macOS, or Schannel on Windows).

### How do I bypass certificate validation for local testing?

Set `accept_invalid_certs: true` in your `HttpConfig` or export `FORGE_HTTP_ACCEPT_INVALID_CERTS=true`. This calls `reqwest::ClientBuilder::danger_accept_invalid_certs(true)`, which disables all certificate validation. **Only use this in development environments**, as it exposes connections to man-in-the-middle attacks.

### Can I configure all HTTP settings through environment variables?

Yes. The `forge_config` crate automatically maps environment variables with the `FORGE_HTTP_*` prefix to `HttpConfig` fields. For example, `FORGE_HTTP_TLS_BACKEND=rustls` sets the backend, while `FORGE_HTTP_ROOT_CERT_PATHS` accepts comma-separated paths to PEM or DER certificate files that augment the system's trust store.

### Where does Forge actually create the HTTP client?

The concrete `reqwest::Client` instantiation occurs in [`crates/forge_infra/src/http.rs`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_infra/src/http.rs) within the `ForgeHttpInfra::new` function. This method extracts `HttpConfig` from the provided `ForgeConfig` and translates the domain model into `reqwest::ClientBuilder` calls, handling TLS version mapping, backend selection, and certificate loading before producing the final client instance.