# `cleveragents.shared` — Secret Redaction Utilities

The `cleveragents.shared` package exposes cross-cutting helpers that can be
used from any layer without violating architecture boundaries. At the moment
its public surface focuses on a single responsibility: deterministic masking of
secrets before they reach logs, error payloads, or tool output.

All exports live in `cleveragents.shared.redaction` and are re-exported from the
package root for convenience.

---

## Quick start

```python
from cleveragents.shared import redact_dict, register_pattern

# redact nested structures before logging
payload = {
    "provider_api_key": "sk-ant-api03-secret-1234567890",
    "token_usage": 1280,
    "upstream": {
        "authorization": "Bearer 0123456789ABCDEFGHIJKLMNOPQRSTUV",
    },
}
print(redact_dict(payload))
# {'provider_api_key': '***REDACTED***', 'token_usage': 1280,
#  'upstream': {'authorization': '***REDACTED***'}}

# extend detection with an internal pattern
register_pattern(r"my-secret-[0-9a-f]{16}")
```

---

## Global redaction controls

| Symbol | Description |
|--------|-------------|
| `REDACTED` | Constant string (`***REDACTED***`) used as the replacement token. |
| `get_show_secrets() -> bool` | Returns the global flag that determines whether redaction is bypassed. |
| `set_show_secrets(value: bool) -> None` | Toggles the flag. Accepts only boolean values and is thread-safe. |

When `get_show_secrets()` returns `True`, the redaction helpers shortcut and
return the original data unmodified—useful for debugging in trusted
environments. The flag is protected by a lock so it can be flipped safely from
CLI options or unit tests.

---

## Key helpers

### `is_sensitive_key(name: str) -> bool`

Heuristically classifies dictionary keys as sensitive (e.g. containing
"token", "secret", "password"). False positives such as `token_count`
are explicitly whitelisted in the implementation.

### `redact_value(value: str) -> str`

Scans a string for known secret patterns (OpenAI, Anthropic, Google keys,
`tok_` IDs, bearer tokens, long hex strings) and replaces each match with
`REDACTED`. Additional patterns can be registered at runtime via
`register_pattern()`.

### `redact_dict(data: Mapping[str, Any], *, show_secrets: bool | None = None) -> dict`

Recursively traverses nested dictionaries and lists, redacting sensitive keys
with `REDACTED` and scrubbing string values via `redact_value`. By default it
honours the global `get_show_secrets()` flag; override it per call with
`show_secrets=True` when you explicitly need the original values.

### `mask_database_url(url: str) -> str`

Masks the password segment of SQL connection URLs. SQLite URLs are returned
unchanged; any `scheme://user:password@host` pattern is rewritten as
`scheme://user:***@host`.

### `register_pattern(pattern: str) -> None`

Allows projects or extensions to add custom regular expressions to the
redaction list. Patterns are compiled under a lock so multiple workers can
register safely. Passing an empty pattern raises `ValueError`.

---

## Structlog integration

`secrets_masking_processor(logger, method_name, event_dict)` implements the
structlog processor contract. It walks every value in the event dictionary,
redacting keys detected by `is_sensitive_key` and values using `redact_value`
and `redact_dict`. The processor is designed to be inserted near the top of the
structlog pipeline so that downstream formatters never observe raw secrets.

```python
import structlog
from cleveragents.shared import secrets_masking_processor

structlog.configure(
    processors=[
        secrets_masking_processor,
        structlog.processors.JSONRenderer(),
    ],
)
```

---

## Thread-safety notes

The module guards both the global `show_secrets` flag and the list of compiled
patterns with locks. You can safely call `set_show_secrets()` or
`register_pattern()` from concurrent tasks without corrupting shared state.

Because all helpers return new dictionaries (never mutating inputs), you may
confidently hand the redacted payloads to logging subsystems or telemetry
pipelines without affecting the original objects used by business logic.
