How Agent-Reach's Configuration System Manages YAML Files and Environment Variable Fallbacks

Agent-Reach stores user-specific configuration in ~/.agent-reach/config.yaml and automatically falls back to uppercase environment variables when YAML keys are missing, using a priority-based lookup chain in the Config class.

The Panniantong/Agent-Reach repository implements a secure, dual-source configuration system that prioritizes persistent YAML settings while allowing temporary overrides via environment variables. This architecture lets developers commit default values to version control while keeping secrets and deployment-specific overrides in environment variables. The system is implemented in agent_reach/config.py and handles directory creation, file permissions, and cascading lookups automatically.

Configuration Storage and File Structure

Agent-Reach persists user settings in a dedicated configuration directory that is created with restrictive permissions on first access.

Secure Directory Creation

When the Config class is instantiated, it calls make_private_dir from agent_reach/utils/paths.py to ensure the ~/.agent-reach/ directory exists. This utility creates the folder with permissions restricted to the owner only, preventing other system users from accessing configuration files that may contain API keys and tokens.

YAML File Location and Permissions

The configuration file is stored at ~/.agent-reach/config.yaml. When the save() method writes data to disk, it explicitly sets 600-style permissions (rw-------), ensuring that only the file owner can read or write the configuration. This security model is critical for protecting sensitive credentials like exa_api_key or openai_api_key that users store in the YAML file.

The Lookup Chain: YAML Precedence with Environment Fallback

The core retrieval logic resides in the get() method within agent_reach/config.py. This method implements a three-tier lookup system:

  1. YAML Data: Check if the key exists in the loaded dictionary from config.yaml
  2. Environment Variable: Convert the key to uppercase and check os.environ
  3. Default Value: Return the user-supplied default if neither source provides a value
def get(self, key: str, default: Any = None) -> Any:
    # 1️⃣  Look in the loaded YAML data

    if key in self.data:
        return self.data[key]
    # 2️⃣  Fallback to an environment variable (uppercase version of the key)

    env_val = os.environ.get(key.upper())
    if env_val:
        return env_val
    # 3️⃣  Return the supplied default if neither source provides a value

    return default

This approach means that a setting defined in config.yaml takes precedence, but if it is absent, the system automatically checks for an environment variable with the same name converted to uppercase.

Configuration Management Methods

Loading and Saving Configuration

The load() method reads the YAML file using yaml.safe_load. If the file is missing or empty, it initializes an empty dictionary, ensuring the application starts without errors even on fresh installations. The save() method persists the in-memory dictionary back to ~/.agent-reach/config.yaml while maintaining the strict 600 permissions.

Retrieving Values with Fallbacks

To retrieve a configuration value with automatic fallback to environment variables, instantiate the Config class and call get():

from agent_reach.config import Config

cfg = Config()
api_key = cfg.get("exa_api_key", default="none")
print(api_key)      # Uses value from ~/.agent-reach/config.yaml if present,

                    # otherwise falls back to the EXA_API_KEY environment variable.

Setting and Deleting Keys

The set() method updates the in-memory configuration and immediately persists it to disk, while delete() removes a key from both the dictionary and the YAML file:

cfg.set("openai_api_key", "sk-xxxxxxxxxxxx")

# The key is now stored in ~/.agent-reach/config.yaml with secure file permissions.

cfg.delete("github_token")   # Deletes the key from the in‑memory dict and saves the file.

Feature-Specific Configuration Requirements

Agent-Reach declares feature-specific requirements in a FEATURE_REQUIREMENTS dictionary (located in agent_reach/config.py), which maps feature names to lists of required configuration keys. For example, the exa_search feature requires an exa_api_key.

The is_configured() method verifies that all required keys are available via the same lookup chain used by get():

if cfg.is_configured("twitter_xreach"):
    print("Twitter integration can be used")
else:
    print("Missing required Twitter credentials")

This validation ensures that applications can check readiness before attempting to use external APIs, preventing runtime errors from missing credentials.

Summary

  • Agent-Reach stores configuration in ~/.agent-reach/config.yaml with 600 permissions (rw-------) to protect secrets.
  • The Config class in agent_reach/config.py loads YAML data using yaml.safe_load and defaults to an empty dictionary if the file is missing.
  • The get() method implements a priority chain: YAML values first, then uppercase environment variables, then user-supplied defaults.
  • Feature requirements are defined in FEATURE_REQUIREMENTS and validated via is_configured(), which uses the same fallback logic.
  • The make_private_dir utility in agent_reach/utils/paths.py ensures the configuration directory is created with owner-only access.

Frequently Asked Questions

How does Agent-Reach prioritize between YAML files and environment variables?

Agent-Reach checks the YAML file first via the get() method. If the key is not found in ~/.agent-reach/config.yaml, it automatically converts the key to uppercase and checks os.environ. Only if neither source has the value does it return the user-supplied default. This means YAML settings always take precedence over environment variables.

What permissions does Agent-Reach set on configuration files?

The configuration directory is created with permissions restricted to the owner only using make_private_dir. When saving the YAML file, the save() method explicitly sets 600-style permissions (rw-------), ensuring that only the file owner can read or write sensitive configuration data like API keys.

Can I use Agent-Reach without creating a YAML file?

Yes. If ~/.agent-reach/config.yaml does not exist, the load() method initializes an empty dictionary and the application continues normally. You can rely entirely on environment variables (uppercase versions of your configuration keys) without ever creating the YAML file, making the system suitable for containerized deployments where environment variables are preferred.

How do I check if a specific feature has all required configuration?

Use the is_configured() method, passing the feature name as defined in FEATURE_REQUIREMENTS. This method checks that all required keys for that feature are present using the same YAML-to-environment fallback logic. For example, cfg.is_configured("exa_search") verifies that exa_api_key is available either in the YAML file or as the EXA_API_KEY environment variable.

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 →