# How Agent Reach Persists Configuration in ~/.agent-reach/config.yaml

> Agent Reach persists configuration in ~/.agent-reach/config.yaml using atomic writes and environment variable fallback. Learn how its secure Config class manages settings.

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

---

**Agent Reach stores user-specific settings in a YAML file at `~/.agent-reach/config.yaml` using a secure `Config` class that handles directory creation, atomic writes with 600 permissions, and environment variable fallback.**

Agent Reach is an open-source Python framework hosted at Panniantong/Agent-Reach that manages user-specific settings through a local configuration file. The persistence logic resides in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py) and ensures sensitive data like API keys are stored securely with restricted file permissions. This implementation is tested in [`tests/test_config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/tests/test_config.py) and relies on PyYAML (declared in [`pyproject.toml`](https://github.com/Panniantong/Agent-Reach/blob/main/pyproject.toml)) for serialization.

## Configuration File Location and Structure

### Default Path Resolution

The configuration system resolves the storage path using Python's `pathlib` module. According to the source code in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py), the framework defines:

- `CONFIG_DIR` as `Path.home() / ".agent-reach"` (line 18)
- `CONFIG_FILE` as `CONFIG_DIR / "config.yaml"` (line 19)

This creates a hidden directory in the user's home folder, following Unix conventions for application-specific data.

## The Config Class Persistence Mechanism

### Lazy Directory Creation with _ensure_dir()

Before writing any data, the private method `_ensure_dir()` ensures the configuration directory exists. Found at lines 37-40 in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py), this method uses `Path.mkdir(parents=True, exist_ok=True)` to create missing parent directories without raising errors if the path already exists. This method is invoked during class initialization and prior to every save operation.

### Loading Existing Configuration

The `load()` method (lines 41-48) handles configuration retrieval by checking for file existence first. If [`config.yaml`](https://github.com/Panniantong/Agent-Reach/blob/main/config.yaml) exists, it parses the content using `yaml.safe_load`; otherwise, it initializes an empty dictionary. This approach prevents `FileNotFoundError` exceptions on first run while safely handling YAML parsing.

### Secure File Writing with Atomic Permissions

The `save()` method implements platform-aware security measures to prevent credential leakage.

On Unix systems (lines 49-60), it uses low-level `os.open()` with flags `O_WRONLY | O_CREAT | O_TRUNC` and mode `0o600` (owner read/write only). This atomic creation prevents race conditions where temporary files might be world-readable.

For Windows compatibility (lines 63-68), the method falls back to standard `open()` when `os.open` flags aren't supported, ensuring cross-platform functionality.

The configuration data is serialized using `yaml.dump()` with `default_flow_style=False` and `allow_unicode=True` for human-readable output.

## Accessor Methods and Environment Integration

### Reading Configuration Values

The `get(key, default)` method (lines 69-78) implements a two-tier lookup strategy:

1. Check the in-memory configuration dictionary
2. Fall back to uppercase environment variables if the key is absent

This allows environment variables to override file-based settings without modifying the YAML.

### Writing and Deleting Keys

The `set(key, value)` method (lines 80-84) updates the internal dictionary and immediately calls `save()` to persist changes to disk. Similarly, `delete(key)` (lines 85-88) removes the specified key and triggers a save operation, ensuring the file system remains synchronized with the in-memory state.

## Feature-Specific Configuration Validation

### Checking Feature Requirements

The `FEATURE_REQUIREMENTS` dictionary maps optional features (like `"exa_search"`) to their required configuration keys (e.g., `["exa_api_key"]`). The `is_configured(feature)` method (lines 90-94) returns `True` only when all required keys exist in the configuration or environment variables.

### Aggregating Feature Status

`get_configured_features()` (lines 95-100) iterates through all defined features and returns those with complete configurations, enabling the application to dynamically enable functionality based on available credentials.

## Security and Privacy Protections

### Credential Masking

The `to_dict()` method (lines 102-108) provides a sanitized view of the configuration by masking values for keys containing sensitive substrings: "key", "token", "password", or "proxy". This prevents accidental credential exposure in logs or debug output.

## Working with Agent Reach Configuration

```python
from agent_reach.config import Config

# Initialize (loads existing config or creates empty one)

cfg = Config()

# Set API key - automatically persisted with 600 permissions

cfg.set('exa_api_key', 'sk-abcdef123456')

# Retrieve value (checks file first, then environment)

api_key = cfg.get('exa_api_key')

# Remove sensitive data

cfg.delete('exa_api_key')

# Verify feature readiness

if cfg.is_configured('exa_search'):
    print("EXA search is ready")

```

## Summary

- Agent Reach persists configuration in `~/.agent-reach/config.yaml` using the `Config` class from [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py)
- The `_ensure_dir()` method creates the directory structure on demand with `exist_ok=True`
- File writes use `os.open()` with `0o600` permissions on Unix to ensure only the owner can read sensitive data
- The `get()` method supports environment variable fallback for containerized deployments
- Feature validation via `is_configured()` ensures required API keys are present before enabling functionality
- `to_dict()` masks sensitive values to prevent credential leakage in logs

## Frequently Asked Questions

### How does Agent Reach handle first-time setup when the config file doesn't exist?

When initializing the `Config` class for the first time, the `load()` method checks for file existence. If `~/.agent-reach/config.yaml` is missing, it initializes an empty dictionary rather than raising an error. The directory and file are created only when `set()` or `save()` is first called, utilizing the `_ensure_dir()` method to create parent directories automatically.

### Why does Agent Reach use file mode 0o600 when saving configuration?

The `save()` method uses `os.open()` with mode `0o600` (owner read/write only) to prevent other users on the system from accessing sensitive credentials like API keys. This creates the file with restrictive permissions atomically at creation time, avoiding race conditions where temporary files might be readable by others. On Windows, where these Unix permissions aren't available, it gracefully falls back to standard file operations.

### Can environment variables override the YAML configuration file?

Yes. The `get()` method implements a priority system where it first checks the in-memory configuration dictionary loaded from the YAML file. If the key is not found, it automatically checks for an uppercase environment variable with the same name. This allows Docker containers and CI/CD pipelines to inject credentials without modifying the local config file.

### How does Agent Reach prevent API keys from appearing in logs?

The `to_dict()` method sanitizes the configuration before returning it by masking any value where the key contains sensitive substrings like "key", "token", "password", or "proxy". When debugging or logging the configuration object, developers should use `to_dict()` rather than accessing the raw internal dictionary to avoid accidentally exposing credentials in log files or error reports.