05 · Adapters
The core knows what a secret is. Something has to go and get the text.
Six packages that fetch text, hand it to the core and carry the answer back. They decide nothing.
The problem it solves
A copied example is a fork you now own
The core repository ships worked examples of wiring a logger or a tracer into the scanner. You could copy the file. From that moment you owned it: no version, no changelog, no notice when the host SDK's contract moved, and no way for the project to fix wiring it had written itself.
Before
Copy pino-redact.mjs out of the examples folder. It works today. Nobody tells you when pino's next major changes the hook signature.
Now
npm install @redact-secret/adapter-pino. A declared pino range, tested at both ends, with its own changelog inside the tarball.
This repository exists to close that gap, without moving the wiring into the core.
Where they sit
Between your host and the engine
The Redact Secret core
It decides what a secret is. The adapter does not.
The packages
Six of them, on two registries
@redact-secret/adapter
The shared base everything else is built on: one primitive for masking a string, and the bounded walker that finds strings inside nested objects. Install it directly only when writing your own integration.
@redact-secret/adapter-pino
Redacts by value, and works alongside pino's own path-based redact. pino cannot see a token inside a message string or an error message; this can.
@redact-secret/adapter-otel
Every string and string-array attribute on a span and its events, redacted before the span reaches the next processor. Attribute names are not allowlisted, so OpenInference and GenAI conventions are covered without hardcoding either.
@redact-secret/adapter-ai-context
A framework-neutral boundary for AI work: user input, tool results, a constructed context and streamed text, sanitized before any of it reaches a model. It names no model vendor, agent framework or transport.
@redact-secret/adapter-mcp
The Model Context Protocol boundary: a tool's result, optionally its arguments, and what a client reads with resources/read. A thin specialization of the package above, adding only the MCP shape. It imports no MCP SDK, even for types.
redact-secret-adapters
Python's standard library has no value-based redaction at all. This filter adds it, and the [otel] extra covers spans.
A masking-callback host such as Langfuse needs no dedicated package: the shared walker is the whole integration.
Versions and host ranges were read from the npm and PyPI registries on 2026-09-28.
The design rule
When in doubt, print a marker
Every adapter shares one primitive for masking a string, and that primitive never lets an error put text on the wire. These markers are public API: they are what a host sees, and they change only in a major version.
| Marker | When |
|---|---|
| [REDACTED:BLOCKED] | A block finding. The entire leaf is replaced, not just the matched span. |
| [REDACTED:ERROR] | Any failure inside the core call, including an uninitialized core. Never the original text, and never the error's own message. |
| [REDACTED:LIMIT_EXCEEDED] | A value past a walk budget. It is never scanned, and never passed through unmasked. |
| [REDACTED:CYCLE] | A self-referencing object. |
The budgets
Plus a 200,000-character cap per string. Elements and keys beyond a limit are dropped, not passed through. Every bound is overridable per call.
Why it stays small
Four things from the core, and nothing else
- initialize()
- Load the engine.
- scanAndRedact()
- And the shape of what it returns.
- findings
- As an array.
- finding.action
- Whether it is
blockorwarn.
That surface is what the declared compatibility range protects. Because these packages are written in TypeScript against the core's own exported types, a change to it fails the build rather than degrading silently.
What “supported” means here
A range you can check, not a guess
Every published adapter states the host range it supports and runs a test against a real instance of that host, at both ends of the declared range, in CI.
| Adapter | Declared range | Verified by |
|---|---|---|
| adapter-pino | pino ^10.0.0 | a real pino logger writing to a captured stream |
| adapter-otel | @opentelemetry/sdk-trace-base ^2.0.0 | a real span passed through onEnd |
| Python logging | CPython >=3.10 | a real logger with the filter attached |
| Python otel | opentelemetry-sdk <2,>=1.16.0 | a real span through a real tracer provider |
A pino major outside the declared range is deliberately not claimed. It may work, but it is untested, and the core holds its detectors to the same rule.
Being honest