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:
- YAML Data: Check if the key exists in the loaded dictionary from
config.yaml - Environment Variable: Convert the key to uppercase and check
os.environ - 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.yamlwith 600 permissions (rw-------) to protect secrets. - The
Configclass inagent_reach/config.pyloads YAML data usingyaml.safe_loadand 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_REQUIREMENTSand validated viais_configured(), which uses the same fallback logic. - The
make_private_dirutility inagent_reach/utils/paths.pyensures 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →