# How TOML Config Flows into ConfigStruct with Environment Variable Overrides in OpenHuman

> Learn how OpenHuman seamlessly integrates TOML configuration and environment variables into its ConfigStruct, ensuring dynamic and flexible settings management with environment variable overrides.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-08-29

---

**OpenHuman loads workspace TOML settings, merges them with built-in defaults, and applies OPENHUMAN_* environment variables to produce the final Config struct, with env vars taking precedence.**

The OpenHuman repository implements a deterministic configuration pipeline that transforms static TOML files into a runtime `Config` struct. This system allows developers to override any setting through environment variables using a predictable naming convention. Understanding this flow is essential for customizing OpenHuman deployments without modifying source code.

## The Three-Stage Configuration Pipeline

The loading process follows a strict precedence order: **TOML → Defaults → Environment Variables**. Each stage is handled by specific modules in `src/openhuman/config/schema/`.

### Stage 1: Parsing the TOML File

In [`src/openhuman/config/schema/load/impl_load.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/load/impl_load.rs), the loader uses the `toml` crate to parse the workspace's [`openhuman.toml`](https://github.com/tinyhumansai/openhuman/blob/main/openhuman.toml). The parsed document converts into internal schema types (`SchemaDefs`) representing the configuration structure before any merging occurs.

### Stage 2: Merging with Defaults

After parsing, the system calls `merge_with_defaults` from [`src/openhuman/config/schema/helpers.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/helpers.rs). This function walks every field of the schema and fills missing values using hard-coded defaults defined in [`src/openhuman/config/schema/defaults.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/defaults.rs). This ensures the configuration is complete even when the TOML file omits optional settings.

### Stage 3: Applying Environment Variable Overrides

The `env_overlay` module in [`src/openhuman/config/schema/load/env_overlay.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/load/env_overlay.rs) inspects process environment variables matching the `OPENHUMAN_<SECTION>_<KEY>` pattern (consistent with the `.env.example` file convention). For each match, it parses and injects the value, overriding both TOML and default settings. This overlay occurs before final `Config` struct construction, guaranteeing that environment variables always take precedence.

## ConfigStruct Definition and Public API

The fully populated data wraps in the `Config` struct defined in [`src/openhuman/config/schema/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/types.rs). This type is re-exported through [`src/openhuman/config/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/mod.rs) as `openhuman::config::Config`, serving as the canonical API for the rest of the system. Components retrieve values via methods like `config.voice_server()` or `config.proxy()`, which delegate to the merged underlying data.

## Runtime Loading and Error Handling

The orchestration happens in [`src/openhuman/config/ops/loader.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/ops/loader.rs) during core initialization. The configuration loads once and caches for subsequent access, avoiding repeated file I/O.

If an environment variable cannot be parsed into the expected type, the loader records a warning but does **not** abort the process. The fallback value—either from the TOML file or the built-in defaults—is preserved, maintaining system robustness against malformed env entries.

### Accessing Configuration at Runtime

Once the core initializes, access the `Config` instance through the core handle:

```rust
use openhuman::config::Config;

// Core builder implicitly loads the config
let core = openhuman::core::CoreBuilder::new()
    .domains(openhuman::core::DomainSet::full())
    .build()
    .await?;

// Access a concrete setting – e.g., the HTTP proxy URL
let proxy_url = core.config().proxy().url();
println!("Proxy: {}", proxy_url);

```

### Overriding Values with Environment Variables

Set variables following the `OPENHUMAN_<SECTION>_<KEY>` pattern before starting the core:

```rust
// Assuming the environment contains:
// OPENHUMAN_PROXY_URL=https://my-proxy.example.com

let cfg: &Config = core.config();
assert_eq!(cfg.proxy().url(), "https://my-proxy.example.com");

```

## Summary

- **TOML parsing** occurs in [`impl_load.rs`](https://github.com/tinyhumansai/openhuman/blob/main/impl_load.rs), converting [`openhuman.toml`](https://github.com/tinyhumansai/openhuman/blob/main/openhuman.toml) into `SchemaDefs`.
- **Default merging** happens via `merge_with_defaults` in [`helpers.rs`](https://github.com/tinyhumansai/openhuman/blob/main/helpers.rs) using values from [`defaults.rs`](https://github.com/tinyhumansai/openhuman/blob/main/defaults.rs).
- **Environment overlays** are applied by [`env_overlay.rs`](https://github.com/tinyhumansai/openhuman/blob/main/env_overlay.rs), matching variables like `OPENHUMAN_PROXY_URL`.
- The final **Config struct** is defined in [`types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/types.rs) and re-exported via [`mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/mod.rs).
- **Error resilience** ensures that invalid environment variables trigger warnings without crashing the system.
- **Lazy initialization** in [`loader.rs`](https://github.com/tinyhumansai/openhuman/blob/main/loader.rs) loads the configuration once during core startup.

## Frequently Asked Questions

### How does OpenHuman determine the precedence of configuration sources?

OpenHuman applies a fixed three-layer precedence: parsed TOML values serve as the base, hard-coded defaults fill any missing fields, and environment variables override both previous layers. This hierarchy is enforced sequentially during the loading pipeline in [`src/openhuman/config/ops/loader.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/ops/loader.rs).

### What naming convention must environment variables follow to override config values?

Environment variables must match the pattern `OPENHUMAN_<SECTION>_<KEY>`, mirroring the structure defined in the `.env.example` file. For example, to override the proxy URL, set `OPENHUMAN_PROXY_URL`. The [`env_overlay.rs`](https://github.com/tinyhumansai/openhuman/blob/main/env_overlay.rs) module maps these names to specific schema paths during initialization.

### What happens if an environment variable contains an invalid value?

The system emits a warning log but continues execution using the fallback value from either the TOML file or the built-in defaults. This design prevents a single malformed environment variable from crashing the entire OpenHuman core, as implemented in the error handling logic of [`env_overlay.rs`](https://github.com/tinyhumansai/openhuman/blob/main/env_overlay.rs).

### Where is the Config struct defined and how do I import it?

The `Config` struct is defined in [`src/openhuman/config/schema/types.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/schema/types.rs) and publicly re-exported through [`src/openhuman/config/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/config/mod.rs). Import it using `use openhuman::config::Config;` to access the fully populated configuration after core initialization.