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:

  1. 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.
  2. 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_KEY for the key exa_api_key).
  3. 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 Config class checks self.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_key becomes EXA_API_KEY), allowing runtime overrides without file modifications.
  • Secure storage: The save() method writes configuration changes with 0o600 permissions, 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →