# How the Config Class Manages Credentials Securely in Agent-Reach

> Learn how the Agent-Reach Config class securely manages credentials. It uses a private YAML file, environment variables, and logs masking to protect API keys and tokens.

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

---

**The Config class stores API keys and tokens in a user-specific YAML file with 0o600 permissions, supports environment variable fallbacks, and masks sensitive values in logs to prevent credential leakage.**

The `Config` class in the Panniantong/Agent-Reach repository provides a lightweight, security-focused configuration manager for handling sensitive credentials. Located in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py), this implementation combines filesystem-level protections with runtime safeguards to ensure API keys and authentication tokens remain confidential while remaining accessible to the application.

## Secure Filesystem Storage

The `Config` class implements defense-in-depth at the storage layer by creating a dedicated hidden directory and enforcing strict file permissions.

### Dedicated Hidden Directory

On first initialization, the class creates `~/.agent-reach` to isolate credentials from the rest of the filesystem. The `CONFIG_DIR` constant defines this path as `Path.home() / ".agent-reach"`, and the `_ensure_dir()` method creates the folder using `mkdir(parents=True, exist_ok=True)`. This prevents accidental exposure through directory listings and keeps sensitive data out of version-controlled project directories.

### Atomic File Permissions

When writing credentials to disk, the `save()` method in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py) uses `os.open` with `stat.S_IRUSR | stat.S_IWUSR` flags to create files with mode **0o600** (read/write for owner only). This ensures that even if the file is created in a shared environment, other users cannot read the contents. For Windows or edge cases where these POSIX flags are unavailable, the code catches `OSError` and falls back to standard file operations while preserving the write operation.

## Flexible Credential Resolution

The class provides multiple access patterns to balance security with convenience, allowing users to avoid persisting secrets on disk when preferred.

### Environment Variable Fallback

The `get()` method implements a lookup hierarchy that checks the in-memory dictionary first, then falls back to environment variables using `os.environ.get(key.upper())`. This allows operators to export sensitive values via shell environment variables (e.g., `export OPENAI_API_KEY=sk-...`) rather than writing them to the YAML file, supporting ephemeral deployment scenarios and CI/CD pipelines.

### Feature-Centric Validation

 Rather than requiring manual credential checking, the `is_configured()` method validates that all required secrets for a specific feature are present. The `FEATURE_REQUIREMENTS` dictionary maps feature names (like `"twitter_xreach"`) to their required key lists (e.g., `["twitter_auth_token", "twitter_ct0"]`). This prevents runtime errors that could expose missing credentials through stack traces or debug output.

## Runtime Data Protection

The `Config` class includes safeguards against accidental credential exposure during logging and debugging.

When converting configuration to a dictionary for display, the `to_dict()` method masks any value where the key contains `"key"`, `"token"`, `"password"`, or `"proxy"`. These sensitive values are truncated to the first **8 characters**, ensuring that debug logs, error reports, and console output never reveal complete secrets while still allowing partial verification that a value is set.

## Practical Implementation Example

```python
from agent_reach.config import Config

# Load (or create) the user config

cfg = Config()

# Store a new API key – file is written with 0o600 permissions

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

# Retrieve a key – prefers the file, then falls back to an env var

api_key = cfg.get("openai_api_key")

# Quick feature check – returns True only if required secrets exist

if cfg.is_configured("openai_whisper"):
    print("OpenAI Whisper is ready to use")
else:
    print("Missing OpenAI credentials")

# Print a safe view (secret values are truncated)

print(cfg.to_dict())

```

## Summary

- The Config class creates a dedicated `~/.agent-reach` directory with restricted permissions to isolate credentials
- File writes use `os.open` with `stat.S_IRUSR | stat.S_IWUSR` to enforce 0o600 mode on POSIX systems, with graceful fallback for Windows
- Environment variables take precedence over file storage via `os.environ.get(key.upper())` lookups in the `get()` method
- The `to_dict()` method masks sensitive values to prevent accidental credential exposure in logs and debug output
- Feature requirements validation via `is_configured()` ensures all necessary credentials are present before API calls

## Frequently Asked Questions

### Where does Agent-Reach store configuration files?

The Config class stores data in `~/.agent-reach/config.yaml` within a hidden directory created under the user's home path. This location is defined by `CONFIG_DIR = Path.home() / ".agent-reach"` in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py), and the folder is created automatically on first use if it does not exist.

### What file permissions does Agent-Reach use for credential storage?

The `save()` method creates files with mode 0o600 (read/write for owner only) using `os.open` with `stat.S_IRUSR | stat.S_IWUSR` flags. On Windows or systems where these POSIX flags are unavailable, it catches `OSError` and falls back to standard file writes without terminating the operation.

### Can I use environment variables instead of the config file?

Yes. The `get()` method checks for environment variables using the uppercase key name before falling back to the YAML file. Set `export OPENAI_API_KEY=your_key` to override file-based credentials and avoid persisting secrets on disk.

### How does Agent-Reach prevent secrets from appearing in logs?

The `to_dict()` method automatically masks values for any key containing "key", "token", "password", or "proxy" by truncating them to the first 8 characters. This ensures that full credentials never appear in debug output, stack traces, or console logs when the configuration object is printed.