How Agent Reach's Config Class Merges Settings from YAML Files and Environment Variables
The Config class in Agent Reach prioritizes YAML file values over environment variables, only falling back to upper-cased environment variables when keys are absent from the configuration file.
The Agent Reach configuration system, implemented in agent_reach/config.py, provides a lightweight mechanism for managing settings across different deployment environments. This utility class resolves configuration values by merging YAML-based files with environment variables using a deterministic precedence order that treats user-maintained files as the authoritative source of truth.
Loading Configuration from YAML Files
When a Config instance is created, the load() method reads the YAML configuration from ~/.agent-reach/config.yaml by default, or from a custom path if specified during instantiation. According to the implementation in agent_reach/config.py, the method uses yaml.safe_load to parse the file contents and stores the resulting dictionary in self.data, providing the foundation for all subsequent lookups.
The loader handles missing files gracefully, allowing the system to operate entirely from environment variables when no configuration file is present.
The Merge Strategy: File Precedence Over Environment
The get(key, default=None) method implements the core merging logic that determines how Agent Reach resolves configuration values. As defined in the source code, this method follows a strict three-step resolution hierarchy:
- YAML File Check: The method first checks if the requested key exists in
self.data. If present, that value is returned immediately, giving file-based configurations top priority. - Environment Variable Fallback: If the key is absent from the file, the system searches for an environment variable with the upper-cased key name (e.g.,
EXA_API_KEYfor the keyexa_api_key). - Default Value: If neither source provides a value, the method returns the supplied default parameter or
None.
This read-only merge strategy ensures that explicit file entries are never overwritten by environment variables, simplifying debugging and maintaining clear configuration boundaries. Users can override missing entries with export EXA_API_KEY=... without editing the YAML file, while existing file values remain protected.
Secure Persistence and Updates
When configuration changes occur via set() or delete() methods, the save() method persists modifications back to the YAML file. The implementation enforces strict file permissions (0o600), ensuring that sensitive credentials like API keys are readable only by the file owner and not exposed to other system users.
This security-conscious approach ensures that even if the configuration contains secrets, they remain protected on disk while the application runs.
Practical Implementation Examples
The following examples demonstrate the precedence behavior when merging YAML files and environment variables:
import os
from agent_reach.config import Config
# Example 1: Value present in the YAML file takes precedence
cfg = Config(config_path="my_config.yaml")
# Assume my_config.yaml contains: exa_api_key: "file-key"
print(cfg.get("exa_api_key")) # → "file-key"
# Example 2: Missing in file falls back to environment variable
os.environ["EXA_API_KEY"] = "env-key"
cfg = Config(config_path="empty.yaml") # empty or non-existent file
print(cfg.get("exa_api_key")) # → "env-key"
# Example 3: Neither source provides a value returns default
print(cfg.get("nonexistent_key", default="fallback")) # → "fallback"
Summary
- File precedence: The
Configclass checksself.data(loaded from YAML) before consulting environment variables, ensuring explicit file configurations act as the source of truth. - Environment fallback: Missing keys trigger a lookup for upper-cased environment variables (e.g.,
exa_api_keybecomesEXA_API_KEY), allowing runtime overrides without file modifications. - Secure storage: The
save()method writes configuration changes with0o600permissions, protecting sensitive credentials from unauthorized access. - Deterministic resolution: The merge strategy is read-only, meaning environment variables never overwrite existing file values, creating predictable configuration behavior.
Frequently Asked Questions
What is the precedence order when Agent Reach merges configuration sources?
Agent Reach follows a strict hierarchy: YAML file values take highest precedence, followed by upper-cased environment variables for missing keys, and finally the default value supplied to the get() method. This ensures that user-maintained configuration files remain the authoritative source while allowing environment-based overrides for unset values.
How does Agent Reach handle sensitive configuration data?
When the save() method writes changes to disk, it sets file permissions to 0o600 (read/write for owner only). This prevents other users on the system from accessing API keys or other credentials stored in the YAML configuration file, even if the underlying filesystem permissions would otherwise allow broader access.
Can environment variables override values in the YAML configuration file?
No, environment variables cannot override existing values in the YAML file. The get() method implements a read-only merge where environment variables are consulted only when a key is absent from self.data. To change a value present in the file, you must edit the YAML directly or use the set() method to update the file contents.
Where does Agent Reach store the default configuration file?
By default, the Config class looks for ~/.agent-reach/config.yaml in the user's home directory. However, you can specify a custom path by passing the config_path parameter when instantiating the Config class, allowing per-project configuration files or alternative storage locations.
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 →