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

> Discover how Agent Reach's Config class prioritizes YAML settings with environment variable fallbacks for flexible configuration management. Learn about persistent vs. override settings.

- Repository: [Pnant/Agent-Reach](https://github.com/Panniantong/Agent-Reach)
- Tags: internals
- Published: 2026-07-09

---

**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`](https://github.com/Panniantong/Agent-Reach/blob/main/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`](https://github.com/Panniantong/Agent-Reach/blob/main/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`](https://github.com/Panniantong/Agent-Reach/blob/main/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:

```python
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`](https://github.com/Panniantong/Agent-Reach/blob/main/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.