How Agent Reach's Config Class Manages YAML and Environment Variable Settings

The Config class implements a YAML-first, environment-variable-fallback strategy, storing persistent settings in ~/.agent-reach/config.yaml while allowing temporary overrides via uppercase environment variables.

The Agent-Reach repository centralizes application settings through a dedicated Config class in agent_reach/config.py. This design prioritizes secure file-based persistence for long-term credentials while maintaining runtime flexibility through environment variable overrides. Understanding this dual-source architecture enables developers to manage sensitive configuration data effectively across different deployment environments.

Configuration Storage and Initialization

The Config class establishes a consistent, secure location for user-specific settings within the home directory structure.

Default Directory Structure

The configuration system defines its storage location using pathlib constants at lines 20–22 in agent_reach/config.py. The default config directory resolves to Path.home() / ".agent-reach", with the specific YAML file located at CONFIG_DIR / "config.yaml". This placement ensures user-specific isolation while maintaining discoverability.

Initialization Process

During instantiation, the __init__ method performs three critical setup steps shown at lines 32–38. First, it resolves the absolute path to the configuration file. Second, it invokes _ensure_dir (which utilizes make_private_dir from agent_reach/utils/paths.py) to create the containing directory with appropriate filesystem permissions if it does not exist. Finally, it calls the load() method to populate the in-memory configuration state from existing YAML data.

YAML File Loading and Persistence

The Config class handles file I/O operations atomically, ensuring data integrity while maintaining strict security controls.

Reading Configuration Files

The load() method at lines 44–48 checks for the existence of the YAML file using os.path.exists. When present, it parses the content using yaml.safe_load and stores the resulting dictionary in self.data. If the file is absent, the method initializes self.data as an empty dictionary, allowing the class to operate purely from environment variables without requiring a pre-existing configuration file.

Secure File Permissions

When persisting changes, the save() method at lines 51–66 writes self.data back to the YAML file using standard file operations. Critically, it creates new files with restrictive permissions set to 0o600 (read/write for owner only), preventing unauthorized access to sensitive API keys and tokens stored within the configuration.

Environment Variable Fallback Mechanism

The Config class implements a hierarchical lookup strategy that respects both persistent files and dynamic environment overrides.

Priority Order in the get() Method

The get(key, default=None) method at lines 75–84 implements a three-tier resolution strategy. First, it checks the in-memory dictionary self.data populated from the YAML file. If the key is absent, it performs a fallback lookup using os.environ.get(key.upper()), converting the key to uppercase to match standard environment variable naming conventions. Only if both sources fail does it return the supplied default value.

Writing Configuration Values

The set(key, value) method at lines 86–90 updates the in-memory dictionary immediately and persists the change to disk by invoking save(). This ensures that all configuration modifications are durable and survive process restarts while maintaining the secure file permissions established during initial creation.

Feature-Specific Configuration Validation

Beyond simple key-value storage, the Config class provides semantic validation for optional features.

Validating Feature Configuration

The class maintains a FEATURE_REQUIREMENTS dictionary (lines 23–30) that maps feature names to lists of required configuration keys. For example, the "exa_search" feature requires ["exa_api_key"]. The is_configured(feature) method at lines 96–99 verifies that all required keys for a given feature return non-None values using the standard get() method, ensuring that both YAML and environment variable sources are considered during validation.

Practical Implementation Examples

The following patterns demonstrate common interactions with the Agent Reach configuration system:

from agent_reach.config import Config

# Initialise (will load ~/.agent-reach/config.yaml if it exists)

cfg = Config()

# 1️⃣ Retrieve a value – prefers YAML, then environment variable

api_key = cfg.get("exa_api_key")          # Returns None if not set

# 2️⃣ Override a setting for the current process via env var

import os
os.environ["EXA_API_KEY"] = "tmp‑key"
print(cfg.get("exa_api_key"))            # Prints "tmp‑key"

# 3️⃣ Persist a new setting to the YAML file

cfg.set("github_token", "ghp_********")   # Writes file with mode 0600

# 4️⃣ Check whether a feature is ready (all required keys present)

if cfg.is_configured("github_token"):
    print("GitHub integration is ready")
else:
    print("Missing GitHub token")

# 5️⃣ Delete a key (removes from YAML and clears any cached value)

cfg.delete("github_token")

Summary

  • The Config class stores persistent data in ~/.agent-reach/config.yaml with 0o600 permissions to protect sensitive values.
  • Environment variables serve as fallback sources when keys are missing from the YAML file, using uppercase key transformations via os.environ.get(key.upper()).
  • The get() method implements a resolution order: YAML data first, environment variables second, default values last.
  • Feature validation through is_configured() checks multiple required keys simultaneously, respecting both configuration sources.
  • All disk operations occur atomically through the save() method, ensuring configuration changes are immediately persistent.

Frequently Asked Questions

Where does Agent Reach store its configuration file?

Agent Reach stores its configuration in a YAML file located at ~/.agent-reach/config.yaml within the user's home directory. The Config class automatically creates this directory and file with secure 0o600 permissions during initialization if they do not already exist.

How does the Config class prioritize between YAML settings and environment variables?

The Config class implements a YAML-first strategy where self.data (loaded from the YAML file) takes precedence. If a key is absent from the YAML data, the get() method automatically falls back to checking for an environment variable with the same name converted to uppercase, as implemented at lines 80–84 in agent_reach/config.py.

What file permissions does Agent Reach use for the config.yaml file?

The save() method creates the configuration file with Unix permissions 0o600 (read and write permissions for the owner only, no permissions for group or others). This security measure ensures that API keys and tokens stored in the YAML file remain accessible only to the user who created them.

How can I check if a specific feature is properly configured?

Use the is_configured(feature) method, which checks whether all required keys for a given feature exist and contain values. This method relies on the standard get() method, meaning it validates against both the YAML configuration file and environment variables. For example, calling cfg.is_configured("exa_search") verifies that exa_api_key is present in either source.

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 →