# Secrets Handling (SEC5)

CleverAgents automatically masks API keys, tokens, and credentials in
CLI output, structured logs, and error messages.  This document covers
the design and usage of the redaction subsystem.

## Overview

The `cleveragents.shared.redaction` module provides centralised,
pattern-based secret detection and masking.  It is integrated into:

- **CLI output** via `format_output()` in `cleveragents.cli.formatting`
- **Error handlers** in `main.py`, `project.py`, and `auto_debug.py`
- **structlog processors** via `secrets_masking_processor`
- **Settings repr** via `Settings.__repr__`

## Detected patterns

| Pattern family | Regex summary | Example |
|---|---|---|
| OpenAI keys | `sk-(?:proj-)?[A-Za-z0-9_-]{10,}` | `sk-proj-abc123…` |
| Anthropic keys | `sk-ant-[A-Za-z0-9_-]{10,}` | `sk-ant-api03-xyz…` |
| Token IDs | `tok_[A-Za-z0-9]{10,}` | `tok_01HXYZ…` |
| Bearer tokens | `Bearer\s+[A-Za-z0-9._~+/=-]{20,}` | `Bearer eyJhb…` |
| Generic keys | `(?:key\|KEY)-[A-Za-z0-9]{20,}` | `KEY-abcdef…` |

Sensitive **key names** (dictionary keys, config fields) are also
detected by substring matching:

`api_key`, `apikey`, `password`, `passwd`, `secret`, `token`,
`credential`, `private_key`, `access_key`, `auth`

False-positive key names like `token_count`, `max_tokens`, etc. are
excluded.

## Replacement token

All detected secrets are replaced with `***REDACTED***`.

## Global `--show-secrets` flag

Pass `--show-secrets` to any CLI command to reveal secrets in that
invocation:

```bash
agents --show-secrets info
agents --show-secrets diagnostics
```

The flag sets a global thread-safe boolean that the redaction layer
checks.  When `True`, all masking is bypassed.

Programmatic access:

```python
from cleveragents.shared.redaction import get_show_secrets, set_show_secrets

set_show_secrets(True)   # reveal secrets
set_show_secrets(False)  # re-enable masking (default)
```

## Custom patterns

Register additional secret patterns at runtime:

```python
from cleveragents.shared.redaction import register_pattern

register_pattern(r"myorg_[a-z0-9]{32}")
```

Patterns are compiled once and appended to the global list.

## Database URL masking

`mask_database_url()` replaces embedded passwords in connection
strings while preserving the host and database name:

```python
from cleveragents.shared.redaction import mask_database_url

mask_database_url("postgresql://user:pass@host/db")
# → "postgresql://user:***@host/db"

mask_database_url("sqlite:///data.db")
# → "sqlite:///data.db"  (unchanged)
```

## structlog integration

The `secrets_masking_processor` is inserted into the structlog
processor chain by `configure_structlog()`:

```python
from cleveragents.config.logging import configure_structlog

configure_structlog(env="production", log_level="INFO")
```

Every log event is scanned for secret patterns before rendering.

## Settings safety

`Settings.__repr__()` masks any field whose name matches a sensitive
key pattern, so accidental `print(settings)` or debug output never
leaks credentials.

## Testing

- **Behave**: `features/security_secrets.feature` (43 scenarios)
- **Robot Framework**: `robot/security_secrets.robot` (10 smoke tests)
- **ASV benchmarks**: `benchmarks/security_secrets_bench.py`
