How the Agent Reach Config Class Prioritizes Config Files Over Environment Variables

The Agent Reach Config class resolves every setting by first checking the YAML configuration file, then falling back to environment variables, and finally returning an optional default value.

The Config class in the Panniantong/Agent-Reach repository serves as the single source of truth for all user-defined settings. According to the source code in agent_reach/config.py, the Agent Reach Config class prioritizes config files over environment variables by enforcing a strict three-layer lookup hierarchy. This design ensures that persistent YAML-based configuration always masks identically-named environment variables unless the file entry is removed.

Configuration Precedence Hierarchy

The Config class defines an explicit lookup order inside agent_reach/config.py. When you request a key, the resolution follows this sequence:

  1. Configuration file — Values loaded from ~/.agent-reach/config.yaml into the internal dictionary self.data.
  2. Environment variables — If the key is absent from self.data, the class searches os.environ for an upper-cased version of the key.
  3. Default argument — If neither source yields a value, the default parameter passed to get() is returned.

Because the file dictionary is checked first with if key in self.data:, any entry stored in the YAML file overrides a matching environment variable automatically.

Loading and Storing File Configuration

On instantiation, the Config class calls its load() method (lines 40–46 of agent_reach/config.py) to read ~/.agent-reach/config.yaml — or a custom path if provided — into the internal self.data dictionary. This dictionary becomes the primary lookup table for all subsequent get() requests.

If the file does not exist or is empty, self.data starts as an empty dictionary, causing the class to skip directly to environment variable resolution.

The get() Method Implementation

The precedence logic lives in the get() method (lines 68–77 of agent_reach/config.py). The implementation performs the following checks in order:

  • It first tests whether the requested key exists in self.data.
  • If not found, it queries os.environ.get(key.upper()).
  • If still unresolved, it returns the user-supplied default.

This design lets users persist credentials and other settings safely in the local YAML file while still supporting temporary overrides via environment variables whenever the file entry is missing.

Modifying and Persisting Settings

The class exposes several helper methods that operate on the same internal dictionary and persist changes back to disk:

  • set(key, value) — Updates self.data and triggers save().
  • delete(key) — Removes a key from self.data and persists the change.
  • is_configured() — Checks whether the configuration has been populated.
  • get_configured_features() — Returns a list of configured feature keys.

All mutations are written immediately to ~/.agent-reach/config.yaml via the save() method, ensuring the file remains the authoritative source.

Practical Code Examples

The following examples demonstrate the precedence behavior in practice:

from agent_reach.config import Config
import os

# -------------------------------------------------

# Example 1 – Normal lookup (file takes priority)

# -------------------------------------------------

# Assume ~/.agent-reach/config.yaml contains:

#   exa_api_key: "file-key-123"

cfg = Config()
print(cfg.get("exa_api_key"))          # -> "file-key-123"

# Even if an env var is set, the file value wins

os.environ["EXA_API_KEY"] = "env-key-456"
print(cfg.get("exa_api_key"))          # still -> "file-key-123"

# -------------------------------------------------

# Example 2 – Fallback to environment variable

# -------------------------------------------------

# Remove the key from the config file (or use a fresh Config instance)

cfg.delete("exa_api_key")
print(cfg.get("exa_api_key"))          # -> "env-key-456"

# -------------------------------------------------

# Example 3 – Providing a default value

# -------------------------------------------------

print(cfg.get("nonexistent_key", default="default-value"))

# -> "default-value"

Summary

  • The Config class in agent_reach/config.py enforces a fixed precedence: config file > environment variable > default.
  • The load() method populates self.data from ~/.agent-reach/config.yaml at instantiation.
  • The get() method (lines 68–77) checks self.data before querying os.environ.get(key.upper()).
  • Any key present in the YAML file automatically masks an identically-named environment variable.
  • Mutations through set() and delete() are persisted back to the YAML file via save().

Frequently Asked Questions

What happens if a key exists in both the config file and an environment variable?

The file entry always wins. Because agent_reach/config.py checks if key in self.data: before querying os.environ, a value stored in ~/.agent-reach/config.yaml completely overrides its environment counterpart.

How does Agent Reach normalize environment variable names?

The get() method upper-cases the requested key when falling back to environment variables. A lookup for exa_api_key becomes a search for EXA_API_KEY in os.environ.

Can I override a file-based setting temporarily without editing the YAML?

No, not through the standard get() path. Since the file takes priority, you must either delete the key from the config file using cfg.delete(key) or modify the YAML directly so that the environment variable is consulted instead.

Where does Agent Reach store configuration data by default?

By default, the Config class reads from and writes to ~/.agent-reach/config.yaml in the user's home directory. This path is loaded during load() at instantiation and updated on every save() call.

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 →