# How Agent Reach Stores and Secures Credentials Like Cookies and Tokens

> Learn how Agent Reach securely stores and validates credentials like cookies and tokens in a private YAML file with strict permissions. Supports environment variable overrides for CI CD.

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

---

**Agent Reach isolates sensitive credentials in a private YAML file at `~/.agent-reach/config.yaml` with strict Unix permissions (0o600), supports environment variable overrides for CI/CD workflows, and validates browser cookie imports before persisting them to disk.**

The [Panniantong/Agent-Reach](https://github.com/Panniantong/Agent-Reach) repository implements a unified credential management system that keeps authentication material—cookies, API tokens, and OAuth secrets—out of the source tree while providing a secure, programmatic interface for channel modules. This design follows security-by-default principles: least-privilege file access, explicit secret loading, and clear audit boundaries between user data and application code.

## The Centralized Config Architecture

All credential storage flows through a single abstraction point defined in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py). This module creates a hidden directory in the user’s home folder and enforces OS-level permissions that prevent other system users from reading authentication material.

### File Structure and Permissions

When the `Config` class initializes, it ensures the storage location exists with owner-only access:

- **Directory**: `~/.agent-reach/` created with mode `0o700` (rwx------)
- **File**: `~/.agent-reach/config.yaml` written with mode `0o600` (rw-------)

The helper `make_private_dir` establishes these constraints immediately upon instantiation, eliminating the risk of accidental world-readable credential files.

### The Config Class Interface

The [`Config`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py) class provides three primary methods for secret management:

- **`get(key, default=None)`**: Retrieves a value by key, first checking an uppercase environment variable of the same name, then falling back to the YAML file. This enables operators to inject secrets via `export KEY=value` without touching the disk.
- **`set(key, value)`**: Persists a key-value pair to [`config.yaml`](https://github.com/Panniantong/Agent-Reach/blob/main/config.yaml) using an atomic write-and-replace strategy, ensuring data integrity even if the process crashes mid-write.
- **`delete(key)`**: Removes a secret and rewrites the file, guaranteeing that revoked credentials leave no residual data in the storage medium.

## Cookie Handling and Validation

Browser cookies represent a high-risk credential type because they often contain session tokens with extended lifetimes. Agent Reach handles these through a dedicated validation layer before storage.

### Browser Cookie Import

The [[`cookie_extract.py`](https://github.com/Panniantong/Agent-Reach/blob/main/cookie_extract.py)](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/cookie_extract.py) module accepts cookie exports from Chrome, Firefox, or manual textual formats. It validates required fields—`name`, `value`, `domain`, and `path`—then concatenates entries into platform-specific strings (e.g., the cookie header format expected by Twitter).

```python

# agent_reach/cookie_extract.py (conceptual flow)

from agent_reach.config import Config

def import_cookies(platform: str, cookie_file: Path):
    config = Config()
    validated = validate_cookie_format(cookie_file)
    cookie_string = format_for_header(validated)
    config.set(f"{platform.upper()}_COOKIE", cookie_string)

```

This approach ensures that malformed or incomplete cookie dumps never reach the persistent store.

### Storage Format and Access

Once validated, cookies are stored as plain key-value pairs in the YAML file. Channels retrieve them at runtime via the standard `Config` interface:

```python

# Example: Retrieving Twitter cookies in a channel implementation

from agent_reach.config import Config

def make_authenticated_request():
    cfg = Config()
    cookie = cfg.get("TWITTER_COOKIE")
    headers = {"Cookie": cookie}
    # ... proceed with HTTP request

```

The credential string never appears in source control, logs, or error traces; it exists only in the private config file and the transient memory of the requesting process.

## Token and API Key Management

Beyond browser cookies, Agent Reach stores API keys and OAuth secrets using identical primitives. The repository designates conventional uppercase keys for each platform:

- **`TWITTER_COOKIE`** / **`TWITTER_CSRF_TOKEN`**
- **`REDDIT_CLIENT_ID`** / **`REDDIT_CLIENT_SECRET`**
- **`GITHUB_TOKEN`**
- **`OPENAI_API_KEY`**

### Environment Variable Fallback

The `Config.get` method implements a precedence hierarchy that prioritizes environment variables over file storage:

```python

# agent_reach/config.py (simplified)

def get(self, key: str, default: Any = None) -> Any:
    env_key = key.upper()
    return os.getenv(env_key) or self._data.get(key, default)

```

This design supports containerized deployments and CI/CD pipelines where operators inject secrets via orchestration secrets rather than mounting host files.

### Channel Integration

Each platform channel (located in `agent_reach/channels/`) imports the `Config` class and requests only the specific keys it requires. For example, a Reddit channel would access credentials like this:

```python
from agent_reach.config import Config

config = Config()
client_id = config.get("REDDIT_CLIENT_ID")
client_secret = config.get("REDDIT_CLIENT_SECRET")

# Use credentials in OAuth flow

```

This compartmentalization ensures that a compromise in one channel module does not automatically grant access to unrelated platform credentials.

## Security Implementation Details

### Permission Hardening

The atomic write mechanism in `Config._save` explicitly sets file permissions before moving the temporary file into place:

```python

# agent_reach/config.py

def _save(self):
    tmp = self.config_path.with_suffix(".tmp")
    with open(tmp, "w", encoding="utf-8") as f:
        yaml.safe_dump(self._data, f)
    os.chmod(tmp, stat.S_IRUSR | stat.S_IWUSR)  # 0o600

    tmp.replace(self.config_path)

```

This guarantees that even if the default umask is permissive, the resulting [`config.yaml`](https://github.com/Panniantong/Agent-Reach/blob/main/config.yaml) remains readable only by the file owner.

### Isolation from Source Code

No credential values are hard-coded in the repository. The `Config` constructor raises sensible defaults (empty dictionaries) if the file is missing, and the `doctor` diagnostic tool (in [`agent_reach/doctor.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/doctor.py)) can report which expected keys are absent without exposing the values of present keys. This architecture satisfies the principle that **credentials are data, not code**, keeping secrets out of Git history and package distributions.

## Practical Usage Examples

### Storing a Twitter Cookie via CLI

After exporting cookies from your browser to a text file, import them securely:

```bash
python -m agent_reach.cli cookie-import \
  --platform twitter \
  --file ~/Downloads/twitter_cookies.txt

```

Behind the scenes, this invokes [`cookie_extract.py`](https://github.com/Panniantong/Agent-Reach/blob/main/cookie_extract.py), validates the format, and executes `Config.set("TWITTER_COOKIE", ...)` with the resulting string.

### Retrieving a GitHub Token

```python
from agent_reach.config import Config

cfg = Config()
token = cfg.get("GITHUB_TOKEN")

import requests
response = requests.get(
    "https://api.github.com/user",
    headers={"Authorization": f"Bearer {token}"}
)

```

### Removing a Revoked Credential

```python
from agent_reach.config import Config

cfg = Config()
cfg.delete("OLD_API_KEY")

# The file is immediately rewritten without the deleted entry

```

## Summary

- **Storage Location**: Credentials reside in `~/.agent-reach/config.yaml`, isolated from the application source tree.
- **Permission Model**: Directory `0o700` and file `0o600` ensure only the owner can read or write secrets.
- **Access Pattern**: The `Config` class in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py) provides `get`, `set`, and `delete` with environment variable fallback.
- **Cookie Workflow**: [`agent_reach/cookie_extract.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/cookie_extract.py) validates browser exports before persisting them via `Config.set`.
- **Security Guarantees**: Atomic writes, explicit permission setting, and no hard-coded defaults prevent accidental credential exposure.

## Frequently Asked Questions

### Where does Agent Reach store my API keys and cookies?

Agent Reach stores all credentials in a YAML file located at `~/.agent-reach/config.yaml`. The `Config` class in [`agent_reach/config.py`](https://github.com/Panniantong/Agent-Reach/blob/main/agent_reach/config.py) manages this path, ensuring the directory is created with `0o700` permissions and the file itself is written with `0o600` permissions, making it accessible only to your user account.

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

Yes. The `Config.get()` method checks for an environment variable matching the uppercase key name before falling back to the file. For example, if you set `export GITHUB_TOKEN=ghp_xxx`, calls to `Config().get("GITHUB_TOKEN")` will return that value without reading the disk, ideal for CI/CD pipelines and containerized deployments.

### How does Agent Reach prevent other users on the system from reading my cookies?

The implementation uses explicit Unix permission bits. The `make_private_dir` function creates the `~/.agent-reach` directory with `0o700` (rwx------), and the `_save` method in `Config` applies `stat.S_IRUSR | stat.S_IWUSR` (0o600) to the temporary file before atomically replacing the live config file. This blocks group and other read access regardless of the system umask.

### What happens if I need to revoke or delete a credential?

You can call `Config().delete("KEY_NAME")` programmatically, or use the CLI equivalent (e.g., `agent_reach config delete --key KEY_NAME`). This method removes the key from the internal dictionary and immediately rewrites the YAML file using the atomic `_save` procedure, ensuring the revoked credential leaves no trace in the storage file.