Skip to content

feat(otel): client-side redaction of PII and/or secrets#584

Merged
simonvdk-mistral merged 19 commits into
mainfrom
svdk/feat/client_side_masking
Jul 6, 2026
Merged

feat(otel): client-side redaction of PII and/or secrets#584
simonvdk-mistral merged 19 commits into
mainfrom
svdk/feat/client_side_masking

Conversation

@simonvdk-mistral

@simonvdk-mistral simonvdk-mistral commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What

Adds a client-side redaction layer for OpenTelemetry spans so PII and secrets never leave the machine. The core primitive is reusable by any OTEL application, and the Mistral SDK installs it automatically when it owns the exporter.

Also add documentation and examples for the observability features of the SDK.

The primitive

RedactingSpanExporter wraps any SpanExporter and redacts each span before delegating the actual export:

from mistralai.extra.observability.redaction import RedactingSpanExporter

exporter = RedactingSpanExporter(OTLPSpanExporter(...))          # default policy
provider.add_span_processor(BatchSpanProcessor(exporter))

Redaction covers the whole span surface: attributes, events, links, resource attributes, span name, and status description.

The module requires the optional OpenTelemetry SDK (the telemetry extra) to run, but not to import — so it stays importable in environments without the extra.

Why an exporter wrapper (and not span-creation-time redaction)

Redaction is deliberately placed in a SpanExporter decorator, invoked at export time, rather than when spans/attributes are created. In dedicated mode the SDK wires:

BatchSpanProcessor(RedactingSpanExporter(OTLPSpanExporter(...)))

This has real, concrete benefits:

  • Off the request hot path. With BatchSpanProcessor (what the SDK installs), export runs on a dedicated background thread, in batches. The regex scanning and span rebuild happen there — not on the threads serving the user's chat/embeddings calls — so redaction adds no latency to application requests. Cost is amortized across a batch and absorbed by the exporter thread; under extreme load it manifests as export backpressure, never as slower API calls.
  • Last line of defense. Sitting at the very edge, right before bytes leave the process, it sees every span regardless of which instrumentation produced it. Nothing is exported without passing through redaction (fail-closed).
  • Composable and reusable. It's a plain SpanExporter decorator with no Mistral coupling, so any OpenTelemetry application can wrap its own exporter — which is exactly what users do in global/custom-provider mode.

Caveat: the "off the hot path" property depends on an asynchronous processor. BatchSpanProcessor (installed by the SDK) gives it; a SimpleSpanProcessor would export — and therefore redact — synchronously on span end.

Policies

Policy Strategy Trade-off
RegexRedactionPolicy (default, redaction=True) Content-oriented: keeps keys and structure, redacts matched substrings (secret tokens plus PII — emails, card-like sequences, IPv4). Redacts most sensitive data while preserving observability value; may miss free-form PII or secrets not in the pattern set.
AttributeRedactionPolicy Key-oriented: redacts whole values for sensitive keys (explicit set, fragment match, or non-primitive value), then scans kept values for secret token patterns. Very conservative, but erases most prompt/response content.
CallbackRedactionPolicy (redaction=<callable>) Your (key, value) -> value | None masker per attribute; return None to drop the attribute. Full control; you own the logic.

SDK integration

configure_telemetry gains a redaction argument:

  • True (default) — default (regex) policy
  • False — redaction disabled
  • a RedactionPolicy instance (e.g. AttributeRedactionPolicy())
  • a (key, value) -> value | None callback

Redaction only applies in dedicated provider mode, where the SDK owns the exporter. In global/custom-provider modes the application owns the export pipeline, so the argument is ignored and a warning is logged — wrap your own exporter with RedactingSpanExporter in that case.

Tests

  • New test_redaction.py covering the policies, span rebuild, and the exporter
    wrapper.
  • Extended test_telemetry.py for the redaction wiring and the
    ignored-argument warnings.

simonvdk-mistral and others added 11 commits July 1, 2026 17:59
Fold AWS, Google, JWT, PEM and Stripe key patterns into
DEFAULT_TOKEN_PATTERNS and compose DEFAULT_PII_SECRET_PATTERNS from it,
with tests covering each secret and the composition invariant.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Use a conditional base (SpanExporter under TYPE_CHECKING, object at
runtime) so linters verify the export/shutdown/force_flush overrides
while keeping the OpenTelemetry SDK an optional import.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Add a shared _redact_value helper so both policies scan the string
elements of list/tuple attribute values instead of passing them through
verbatim, preserving the container type and leaving numeric/bool
sequences untouched.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Rewrite test_redaction.py as pytest classes with fixtures and
parametrization, and convert the TestTelemetryRedaction class in
test_telemetry.py to a plain pytest class (using caplog for log
assertions). Optional span attributes/context are narrowed with
asserts instead of file-level pyright suppressions. Older telemetry
tests are left as-is.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@gitguardian-eu

gitguardian-eu Bot commented Jul 2, 2026

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@simonvdk-mistral

Copy link
Copy Markdown
Contributor Author

⚠️ GitGuardian has uncovered 2 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request

GitGuardian id GitGuardian status Secret Commit Filename
289842 Triggered Shopify Generic App Token e8f0122 src/mistralai/extra/tests/test_redaction.py View secret
289840 Triggered PostHog Project API Key e8f0122 src/mistralai/extra/tests/test_redaction.py View secret
🛠 Guidelines to remediate hardcoded secrets

  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider

🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

These are all fake/dummy secrets to test redaction

Switch default_redaction_policy() from the key-oriented
AttributeRedactionPolicy to the content-oriented RegexRedactionPolicy,
which preserves keys/structure and redacts only matched secret/PII
substrings. Update docstrings, README policy table, the example, and
adapt the behavioural tests accordingly.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@simonvdk-mistral simonvdk-mistral requested a review from a team July 2, 2026 17:23
@simonvdk-mistral simonvdk-mistral marked this pull request as ready for review July 2, 2026 17:24
Comment thread src/mistralai/extra/observability/redaction.py Outdated
Comment thread examples/mistral/observability/redaction_policies.py Outdated

@rbarbadillo rbarbadillo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally lgtm! just a small edge-case to cover

Comment thread src/mistralai/extra/observability/telemetry.py Outdated
simonvdk-mistral and others added 5 commits July 6, 2026 11:16
Regression test for the case where a second configure_telemetry call in
dedicated mode is ignored because of the early return on an existing
auto provider. Currently fails: the provider is not rebuilt so the new
redaction setting never takes effect.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
The early return on an existing auto telemetry provider fired before the
replace_existing check, so an explicit configure_telemetry call (which
always requests replacement) was a no-op: the provider was never rebuilt
and a changed redaction setting was silently ignored. Only short-circuit
when replace_existing is False, letting the rebuild path shut down the
old provider and recreate it with the new settings.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@simonvdk-mistral simonvdk-mistral merged commit e48caf6 into main Jul 6, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants