How Agent Reach Stores Credentials Securely in config.yaml with 0600 Permissions

Agent Reach stores all API keys and tokens in ~/.agent-reach/config.yaml with atomic file creation and strict 0600 permissions, ensuring only the file owner can read or write sensitive data.

When building CLI tools that handle third-party API keys, protecting secrets from unauthorized access is critical. According to the Agent Reach source code, the project implements a defense-in-depth strategy that combines filesystem permissions, atomic writes, and runtime masking to keep credentials secure while maintaining usability.

Understanding the Config Storage Location

All sensitive configuration data resides in a user-specific directory located at ~/.agent-reach/. Within this directory, the config.yaml file serves as the single source of truth for API keys, access tokens, and integration secrets.

The Config class defined in agent_reach/config.py (lines 18-20) manages this file exclusively. When initialized, it automatically determines the correct path based on the user's home directory, ensuring that credentials are never stored in system-wide locations accessible to other users.

Atomic File Creation with Permission Hardening

The core security mechanism resides in the save() method implemented in agent_reach/config.py (lines 51-60). Rather than using standard file operations that could temporarily expose data, Agent Reach employs low-level system calls to guarantee permissions are set at creation time—before any data hits the disk.

The implementation uses os.open() with specific flags:

import os
import stat

# Flags: Write-only, create if not exists, truncate if exists

flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC

# Mode: Owner read/write only (octal 0600)

mode = stat.S_IRUSR | stat.S_IWUSR

fd = os.open(filepath, flags, mode)

This approach eliminates race conditions where the file might exist with world-readable permissions (such as 0644 or 0640) even for a fraction of a second. If the underlying operating system does not support these specific flags—for example, on Windows—the code gracefully falls back to a standard open() call, though the primary path ensures Unix-like systems maintain strict security boundaries.

Security Mechanisms

Agent Reach employs three distinct pillars to protect sensitive configuration data.

User-Only Storage Location

The configuration directory lives under the user's home directory (~/.agent-reach), ensuring that the operating system's standard user isolation prevents other system users from accessing the directory contents. This provides the first layer of defense through standard Unix file ownership models.

Permission-Hardening at Creation

By creating config.yaml with 0600 permissions (owner read/write only), the save() method ensures no other user or group can access the file contents. This is implemented through bitwise OR operations combining stat.S_IRUSR and stat.S_IWUSR, which translates precisely to octal 0600.

Masking Sensitive Values

When configurations are displayed in logs or CLI output, the to_dict() method (lines 101-108 in config.py) automatically masks sensitive values. This prevents accidental credential leakage through shell history, log files, or terminal output, ensuring that even if a user prints the configuration object, secrets remain hidden.

Working with Credentials

The Config class provides a simple interface for storing and retrieving credentials while automatically enforcing security controls.

Saving a new API key (automatically enforces 0600 permissions):

from agent_reach.config import Config

cfg = Config()
cfg.set("exa_api_key", "sk-XXXXXXXXXXXXXXXX")

Loading a credential (checks file first, then environment variables):

cfg = Config()
exa_key = cfg.get("exa_api_key")  # Returns the stored key or None

Inspecting the stored config without revealing secrets:

cfg = Config()
print(cfg.to_dict())

# Output shows masked value like "exa_api_key": "sk-XXXX..."

Summary

  • Agent Reach stores credentials in ~/.agent-reach/config.yaml with strict 0600 permissions enforced at file creation.
  • Atomic writes using os.open() with O_WRONLY | O_CREAT | O_TRUNC and mode stat.S_IRUSR | stat.S_IWUSR prevent race conditions where files might be temporarily exposed.
  • Automatic masking in to_dict() prevents credential leaks through logs and terminal output.
  • Test coverage in tests/test_config.py validates correct loading, saving, and permission behavior across platforms.

Frequently Asked Questions

What does 0600 permission mean in Agent Reach?

The 0600 permission setting means only the file owner has read and write access, while group members and other users have no permissions whatsoever. In Agent Reach, this is implemented using stat.S_IRUSR | stat.S_IWUSR when calling os.open() in the save() method, ensuring secrets are never world-readable.

How does Agent Reach prevent credentials from being exposed during file creation?

Agent Reach uses atomic file creation via os.open() with flags O_WRONLY | O_CREAT | O_TRUNC combined with the 0600 mode mask. This ensures the file is created with correct permissions from the very first moment it exists on the filesystem, eliminating the window where temporary files might be readable by others.

Can I use environment variables instead of config.yaml for sensitive data?

Yes. The Config.get() method checks environment variables as a fallback mechanism. If a key is not found in config.yaml, the system looks for corresponding environment variables, allowing you to keep credentials entirely outside the filesystem if your security policy requires it.

Where does Agent Reach store the config.yaml file by default?

The configuration file is stored at ~/.agent-reach/config.yaml relative to the user's home directory. This location is determined by the Config class initialization logic in agent_reach/config.py, ensuring user-specific isolation regardless of where the Python process is executed.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →