How Agent Reach Securely Stores and Retrieves Credentials: A Complete Technical Guide
Agent Reach stores credentials in a YAML file at ~/.agent-reach/config.yaml with strict 600 file permissions and a 700 directory, retrieving values from this encrypted-at-rest location while allowing environment variable overrides.
Agent Reach, an open-source framework maintained in the Panniantong/Agent-Reach repository, implements a defense-in-depth approach to secret management. The configuration system ensures that API keys, tokens, and other sensitive data remain accessible to the user while protected from unauthorized access through filesystem permissions and secure I/O operations.
Private Configuration Directory Creation
When instantiating a Config object, Agent Reach immediately establishes a secure workspace. The __init__ method calls _ensure_dir(), which delegates to make_private_dir() in agent_reach/utils/paths.py to create the ~/.agent-reach directory.
This utility applies 700 permissions (read, write, and execute for the owner only) using os.mkdir() with a restrictive mode. According to the source code at lines 10-16, this prevents other users on the system from listing or accessing the configuration folder, establishing the first layer of the security boundary.
Atomic File Creation with Restrictive Permissions
The save() method in agent_reach/config.py (lines 55-73) implements platform-specific secure writing to prevent race conditions where credentials might be temporarily world-readable.
On supported platforms, the code uses os.open() with flags that atomically create the file with 600 permissions (read/write for owner only). If the platform does not support these flags, Agent Reach falls back to a standard open() call followed immediately by chmod 0o600. This eliminates the window between file creation and permission restriction where secrets could be exposed.
Loading and Retrieving Credentials
The load() method (lines 44-49) reads the YAML configuration using yaml.safe_load(), initializing an empty dictionary if the file does not yet exist.
When retrieving values via get() (lines 75-84), Agent Reach implements a hierarchical lookup strategy:
- First, check the in-memory dictionary for the key
- If not found, check for an environment variable with the same name in uppercase
This design allows users to override persistent credentials with temporary environment variables, supporting CI/CD pipelines and shared development environments without writing secrets to disk.
Persisting New Secrets Safely
The set(key, value) method (lines 86-90) ensures that updates are immediately committed to disk. When you store a new API key or token, the method updates the in-memory dictionary and invokes save(), writing the file with the same strict 600 permissions used during initial creation.
This immediate persistence prevents data loss while maintaining the security posture, ensuring that even freshly added credentials receive the same filesystem protection as existing entries.
Masking Secrets in Output and Logs
To prevent accidental exposure in logs, stack traces, or CLI output, the to_dict() method (lines 108-128) implements automatic masking. Any configuration key containing sensitive keywords—such as "key", "token", or "secret"—has its value truncated to the first eight characters followed by ....
For example, an OpenAI API key appears as sk-xxxxx... rather than the full string, protecting against shoulder surfing or accidental copy-paste into support tickets.
Practical Implementation Example
from agent_reach.config import Config
# Initialize (creates ~/.agent-reach with 700 permissions if needed)
cfg = Config()
# Store a secret - saved with 0o600 permissions atomically
cfg.set("openai_api_key", "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx")
# Retrieve (checks config.yaml first, then OPENAI_API_KEY env var)
api_key = cfg.get("openai_api_key")
# Display safely (automatically masked in output)
print("Config:", cfg.to_dict()) # Shows: {'openai_api_key': 'sk-xxxxx...'}
Summary
- Agent Reach stores credentials in
~/.agent-reach/config.yamlwith owner-only permissions - The configuration directory is created with
700permissions viamake_private_dir()in agent_reach/utils/paths.py - The save() method uses
os.open()with atomic600permission flags or falls back to immediatechmod 0o600 - Retrieval prioritizes the YAML file but allows environment variable overrides via the
get()method - Masking logic in
to_dict()prevents secret leakage in logs by truncating sensitive values
Frequently Asked Questions
Where does Agent Reach store configuration files?
Agent Reach persists all user-specific settings to ~/.agent-reach/config.yaml in the user's home directory. This location is determined at runtime and created automatically with private permissions when the Config class is first instantiated.
What file permissions does Agent Reach use for credentials?
The framework applies the principle of least privilege: the directory receives 700 permissions (owner read/write/execute only), while the configuration file itself receives 600 permissions (owner read/write only). These restrictions are enforced atomically during file creation to prevent race conditions.
Can I override stored credentials with environment variables?
Yes. The get() method in agent_reach/config.py implements a fallback mechanism that checks for environment variables using the uppercase version of the key name. This allows temporary overrides without modifying the persisted YAML file, ideal for ephemeral environments and secret injection.
How does Agent Reach prevent secrets from appearing in logs?
The to_dict() method automatically masks any values associated with keys containing "key", "token", or "secret", displaying only the first eight characters followed by ellipsis. This ensures that accidental print() statements or debug logs do not expose full API keys or authentication tokens.
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 →