Metadata-Version: 2.5
Name: redact-secret-adapters
Version: 0.1.0
Summary: Host integrations for Redact Secret: stdlib logging and OpenTelemetry adapters over the deterministic core.
Project-URL: Homepage, https://github.com/redact-secret/redact-secret-adapters
Project-URL: Repository, https://github.com/redact-secret/redact-secret-adapters
Project-URL: Issues, https://github.com/redact-secret/redact-secret-adapters/issues
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security
Classifier: Topic :: System :: Logging
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: redact-secret<0.2,>=0.1.0b6
Provides-Extra: otel
Requires-Dist: opentelemetry-sdk<2,>=1.16.0; extra == 'otel'
Provides-Extra: test
Requires-Dist: pytest>=8; extra == 'test'
Description-Content-Type: text/markdown

# redact-secret-adapters

Host integrations for [Redact Secret](https://github.com/redact-secret/redact-secret)
in Python: value-based redaction for the standard library's `logging`, plus the
shared fail-closed walker.

```bash
pip install redact-secret redact-secret-adapters
```

## `logging`

```python
import logging
from redact_secret_adapters.logging_filter import RedactSecretFilter

handler = logging.StreamHandler()
handler.addFilter(RedactSecretFilter())
logging.getLogger().addHandler(handler)
```

The filter formats `msg` with `args` before scanning, then clears the
arguments so a downstream formatter cannot rebuild the original. It replaces
`exc_info` with redacted traceback text, scans cached `exc_text` and
`stack_info`, and redacts any `extra_fields=[...]` you name.

Attach it to each emitting **handler**: ancestor logger filters do not run for
propagated child records.

`RedactSecretFilter(scan_and_redact, ...)` accepts an injected scanner; with no
argument it uses `redact_secret.scan_and_redact`.

## Masking callbacks (Langfuse and similar)

```python
from redact_secret_adapters.mask_secrets import mask_secrets

langfuse = Langfuse(mask=mask_secrets)
```

## Fail-closed markers

| Marker | When |
| --- | --- |
| `[REDACTED:BLOCKED]` | A `block` finding — the **entire** leaf is replaced |
| `[REDACTED:ERROR]` | Any exception from the core. Never the input, never the exception's message |
| `[REDACTED:LIMIT_EXCEEDED]` | A value past a walk budget; never scanned, never passed through |
| `[REDACTED:CYCLE]` | A self-referencing object |

`DEFAULT_LIMITS`: `max_depth` 8, `max_array_length` 1000, `max_object_keys` 200,
`max_string_length` 200000, `max_total_leaves` 5000.

## OpenTelemetry (`[otel]` extra)

```python
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from redact_secret_adapters.otel import create_redacting_span_processor

provider = TracerProvider()
provider.add_span_processor(create_redacting_span_processor(BatchSpanProcessor(otlp_exporter)))
```

Every string and string-sequence attribute on a span and its events is
redacted before the span reaches the next processor, including OpenInference
and GenAI semantic-convention attributes, without hardcoding either
convention's attribute list. `opentelemetry-sdk` never hands a processor a
public, mutable view of a span's attributes; the adapter reaches into the
private `_attributes` field, and past that into its backing `_dict` to get
past the SDK's own immutability guard on frozen attribute bags. See
`redact_secret_adapters.otel` for why that's the SDK's own accepted
mechanism, not a version-specific hack.

Supported range: `opentelemetry-sdk>=1.16.0,<2` — CI runs
`tests/test_otel_host.py`, a real `TracerProvider`/exporter round trip, at
both ends of that range on every run.

## Development

```bash
pip install -e "./python[otel,test]"
pytest
```

The fixture tests read the same JSON files in the repository's `fixtures/`
directory as the TypeScript suite; that shared file is what keeps the two
languages equivalent.

## License

MIT
