rtb-redact¶
Strips secrets out of free-form strings before they reach telemetry, distributed logs, or any third-party observability surface. The rule set runs in a fixed order and is deliberately conservative — it errs toward over-redaction.
It works by shape, not by value: it holds no list of your secrets and recognises credentials by what they look like, so it catches the token an upstream error quoted back at you as readily as the one you were holding.
Part of the phpboyscout Rust toolkit; extracted from — and battle-tested by — rust-tool-base.
Minimum supported Rust version 1.82. Two runtime dependencies, regex and
phf. No unsafe — the crate root carries #![forbid(unsafe_code)].
Public API¶
use rtb_redact::string;
let scrubbed = string("connect to postgres://app:hunter2@db/mydb");
assert!(scrubbed.contains("[redacted]"));
assert!(!scrubbed.contains("hunter2"));
| Item | Purpose |
|---|---|
string |
Redact a &str, returning Cow — Borrowed when nothing matched (no allocation on the common case), Owned otherwise. |
string_into |
Same rules, writing into a caller-supplied String so a buffer can be reused. Clears the buffer first — it replaces the contents, it does not append. |
SENSITIVE_HEADERS |
phf::Set of the ten header names whose values must be redacted at DEBUG/TRACE. O(1) lookup. |
is_sensitive_header |
ASCII-case-insensitive membership test against SENSITIVE_HEADERS. |
redact_header_value |
Mask a single header value: [redacted] for anything non-empty, "" for empty. |
Every signature, default and edge case is in the API reference. The rustdoc is on docs.rs/rtb-redact.
There is nothing to configure: no feature flags, no options struct, no way to add a pattern at runtime.
What gets redacted¶
string strips URL userinfo, common credential query parameters,
Bearer/Basic/Token credentials, PEM private-key blocks, well-known
provider prefixes (sk-, sk-ant-, ghp_, glpat-, AIza, AKIA, Slack
xox…, SendGrid SG.), JWTs of 100 characters or more, and whitespace-bounded
opaque runs of 40 characters or more. Everything it replaces becomes the literal
[redacted].
A fast pre-check bails out before allocating when no anchor character or keyword is present, so the hot path on clean strings is cheap.
The full catalogue — patterns, order, thresholds, and what each rule does not match — is in Redaction rules.
What it does not do¶
Worth knowing before you rely on it:
- It is not a guarantee. A bespoke credential format matches nothing, and a secret shorter than the thresholds slips through.
- JSON and YAML are not parsed.
password=hunter2is redacted;"password": "hunter2"is not. - Header values are not redacted by
string. That is a separate, explicit call — see Redact HTTP headers. - It over-redacts. A bare git commit hash is 40 characters and becomes
[redacted].
The full list, with reasoning, is in What rtb-redact does not do.
Where it's wired¶
rtb-telemetryappliesstringautomatically to an event'sargsanderr_msgbefore an out-of-process sink serialises them.attrsare not covered — callers own redaction for attribute values.SENSITIVE_HEADERSenumerates the headers to redact at DEBUG and is available for an HTTP-client middleware to apply — until a shared client middleware exists in the framework, redact headers at the call site.- Any code emitting free-form strings to an external surface should
route through
stringfirst — see the how-to.
This crate complements secrecy::SecretString (which prevents typed
secrets from being formatted): rtb-redact is the safety net for strings
that were assembled and might contain a secret no type system caught.
Where to go next¶
The documentation follows the Diátaxis framework:
- Tutorial — Scrub your first log line:
fifteen minutes, from
cargo newto a redacted connection string, including what still gets through. - How-to guides
- Reference
- Explanation
How this differs from the Go module¶
gitlab.com/phpboyscout/go/redact solves the same problem for the Go toolkit,
and the two are not bug-for-bug identical. The differences you are most
likely to notice:
rtb-redact |
go/redact |
|
|---|---|---|
| Opaque-run threshold | 40 characters — a git SHA-1 is redacted | 41 characters — a git SHA-1 survives |
| Replacement marker | [redacted] everywhere |
<redacted>, *** and <redacted-token>, by rule |
| Provider match | whole token replaced | prefix kept, body masked (sk-***) |
| Formats covered | no github_pat_, glrt-, gldt- |
all three covered |
| JSON credential values | not matched | matched by a dedicated rule |
| Runtime dependencies | regex, phf |
standard library only |
Do not assume a line redacted by one comes out the same from the other.
Further reading¶
The blog carries a curated route through this subject: Rust, and what survived the port collects everything written about it, ordered so you can start at the beginning rather than newest-first.
Ask phpbotscout

He answers questions about the projects over on the Discord, citing the docs where they already cover it, and offering to raise an issue where they don't. Bring a bug, an idea, or a questionable engineering decision.