How the Config Class Handles YAML File Storage and Environment Variable Lookups in Agent-Reach
The Config class loads default settings from a YAML file and overlays values from environment variables using the AGENT_REACH_<SECTION>_<KEY> naming convention, allowing secrets to override persistent configuration at runtime.
The Config class in Agent-Reach serves as the central configuration manager for the runtime environment. It implements a dual-source strategy that persists user settings in human-readable YAML files while allowing sensitive credentials and temporary overrides via environment variables. This design ensures that the repository remains free of secrets while maintaining straightforward configuration management.
The Three-Phase Configuration Process
The implementation in agent_reach/config.py processes configuration through three distinct phases during initialization.
Phase 1: Loading the YAML Foundation
On instantiation, the class attempts to read a config.yaml file from the current directory, or accepts a custom path via the --config CLI flag. The file contents are parsed using yaml.safe_load and stored in the private attribute self._data as a nested dictionary. This structure mirrors the logical sections of the application, such as channels, doctor, and install, providing sensible defaults for all non-sensitive settings.
Phase 2: Applying Environment Variable Overrides
Immediately after YAML parsing, the private method _apply_env_overrides() scans every configuration entry for a matching environment variable. The naming convention follows AGENT_REACH_<SECTION>_<KEY>, where both the section and key names are upper-cased and underscored. When a match exists, the environment variable's value overwrites the YAML value, enabling secure injection of API keys and tokens without modifying tracked files.
The conversion logic respects basic types: strings containing "true" or "false" become bool, numeric strings convert to int or float, and all other values remain as str.
Phase 3: Providing Typed Accessors
The public API exposes __getitem__ and get() methods for dictionary-style access, along with type-safe accessors including as_bool(), as_int(), and as_str(). These helper methods walk the nested dictionary safely and raise a clear ConfigError when a key is missing or contains an incompatible type.
YAML File Storage Implementation
Persistent configuration resides in config.yaml by default. The class handles file I/O through dedicated helpers that serialize the in-memory dictionary back to YAML when save() is called. This persistence mechanism preserves the structure and comments where possible, though it specifically maintains keys that originated from environment variables by leaving them untouched in the file. This separation ensures that secrets injected via AGENT_REACH_* variables never accidentally leak into version-controlled configuration files.
Environment Variable Resolution
Environment variables act as a dynamic overlay layer. Before any component reads its configuration, the Config instance checks the process environment for overrides. This mechanism proves essential for CI pipelines, Docker containers, and the agent-reach install --env=auto wizard, all of which can inject credentials without filesystem mutations.
Users can export variables for single-command execution:
AGENT_REACH_TWITTER_API_KEY=xyz python -m agent_reach.cli tweet
The value from the environment wins over whatever is stored in config.yaml, and the type coercion ensures that flags like AGENT_REACH_CORE_DEBUG=true correctly resolve as boolean True rather than the string "true".
How the Two Sources Interact
The interaction between file-based and environment-based configuration follows a strict precedence order:
- Startup Defaults – If
config.yamlexists, it provides baseline values for all non-sensitive settings like channel enable flags and default timeouts. - Secret Injection – Environment variables matching the
AGENT_REACH_*pattern overlay the YAML data before any component accesses the configuration. - Runtime Persistence – When commands like
doctororinstallcallconfig.save()after modifying settings (such as after OAuth flows), the method serializes the current in-memory state back to YAML. Keys that existed only in the environment remain absent from the file, maintaining the security boundary.
This architecture keeps secret material out of the repository while giving users a straightforward way to customize the tool for their workflows.
Practical Code Examples
The following patterns demonstrate typical usage of the Config class:
from agent_reach.config import Config
# Load configuration (defaults to ./config.yaml)
cfg = Config()
# Retrieve a string value that may come from YAML or env var
twitter_key = cfg.as_str("channels", "twitter", "api_key")
# Access a boolean flag with type safety
debug_mode = cfg.as_bool("core", "debug")
# Modify settings programmatically and persist
cfg["core"]["log_level"] = "INFO"
cfg.save() # Writes back to config.yaml
The test suite in tests/test_config.py validates these behaviors, ensuring that YAML-defined values return correctly, environment variables override files, and type-casting helpers function as expected.
Summary
- YAML provides stable defaults that are version-controlled and human-editable, loaded via
yaml.safe_loadintoself._data. - Environment variables provide a secure, mutable overlay following the
AGENT_REACH_<SECTION>_<KEY>naming convention, processed by_apply_env_overrides(). - The
Configclass merges both sources at startup, with environment variables taking precedence over file values. - Typed accessors (
as_str,as_bool,as_int) guarantee that callers receive the expected Python types. - The
save()method persists modifications back to YAML while preserving the separation between file-based and environment-only configuration.
Frequently Asked Questions
What is the naming convention for environment variables in Agent-Reach?
Environment variables must follow the pattern AGENT_REACH_<SECTION>_<KEY>, where both the section and key names are converted to uppercase with underscores. For example, a YAML key channels.twitter.api_key becomes AGENT_REACH_CHANNELS_TWITTER_API_KEY.
Does an environment variable override the YAML configuration value?
Yes. When _apply_env_overrides() executes during initialization, it checks for environment variables matching the naming convention. If found, the environment value overwrites the value from config.yaml, allowing temporary overrides and secret injection without file modifications.
What happens to environment-only settings when I save the configuration?
The save() method serializes the current in-memory dictionary back to YAML but preserves keys that were only present in the environment by leaving them untouched in the file. This ensures that secrets injected via environment variables do not accidentally get written to disk in the configuration file.
Where is the Config class implemented in the source code?
The complete implementation resides in agent_reach/config.py, which contains the class definition, file I/O helpers, environment overlay logic, and typed accessor methods. Unit tests demonstrating the YAML and environment variable interaction are located in tests/test_config.py.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →