API reference¶
The crate exports five public items and nothing else. This page states what each one does, what it returns for awkward input, and what it allocates. The rules those functions apply are catalogued separately in Redaction rules.
Everything here is drawn from src/lib.rs at version 0.6.3.
Crate facts¶
| Crate name | rtb-redact (import path rtb_redact) |
| Minimum supported Rust version | 1.82 |
| Edition | 2021 |
| Runtime dependencies | regex, phf — two, not zero |
unsafe |
#![forbid(unsafe_code)] in the crate root |
| Replacement marker | the literal string [redacted] |
| Feature flags | none — there is nothing to switch on or off |
| Configuration | none — the rule set is fixed at compile time |
There is no builder, no options struct, and no way to add, remove or reorder a rule from outside the crate. If you need a pattern the crate does not carry, see Cover a secret the rules miss.
string — redact a &str¶
Applies every rule in the catalogue to input and returns the
result. Marked #[must_use]; ignoring the return value redacts nothing, because
input is not modified.
Returns Cow::Borrowed — no allocation — when the input came through
untouched. That covers three cases: an empty input, an input that contains none
of the anchor characters, and an
input where the anchor check passed but no rule actually changed anything.
Returns Cow::Owned when at least one rule replaced something.
use rtb_redact::string;
let out = string("connect to postgres://app:hunter2@db/mydb");
assert_eq!(out, "connect to postgres://[redacted]@db/mydb");
Allocation: one String for the initial copy, plus one more for each rule that
matches. A clean string allocates nothing at all.
string never panics. All seven patterns are compiled once from string literals
in a LazyLock, so there is no user-supplied pattern to fail at runtime and no
lock contention after first use. Calling it from several threads is safe.
string_into — redact into a buffer you own¶
Writes the redacted form of input into out. It clears out first, so
this replaces the buffer's contents — it does not append to them:
use rtb_redact::string_into;
let mut buf = String::from("previous line");
string_into("Authorization: Bearer sk-ant-api03-abcdefghijklmnop", &mut buf);
assert_eq!(buf, "Authorization: Bearer [redacted]");
An empty input leaves out empty.
The saving over string is real only on the clean path, where the input is
copied straight into the buffer you already allocated. As soon as a rule matches,
the implementation builds a fresh String for that rule's output and moves it
into out, so a redacting call still allocates — once per matching rule. Reach
for string_into when most of your traffic is clean and you are calling it in a
tight loop; otherwise string is simpler and no slower.
SENSITIVE_HEADERS — the header-name set¶
The ten HTTP header names whose values should never be logged. It is a compile-time perfect-hash set, so membership is O(1) and costs no startup work.
| Header name |
|---|
authorization |
proxy-authorization |
cookie |
set-cookie |
x-api-key |
x-auth-token |
x-amz-security-token |
x-goog-api-key |
x-anthropic-api-key |
x-openai-api-key |
Every entry is stored lowercase and the set itself is case-sensitive. Match
against it through is_sensitive_header,
which does the lowercasing for you; a direct SENSITIVE_HEADERS.contains("Authorization")
returns false.
The set is fixed. There is no registration hook, so a header your service treats
as a credential — x-gitlab-token, x-hub-signature, a bespoke internal one —
is not in it and will not be reported as sensitive.
is_sensitive_header — test a header name¶
ASCII-case-insensitive membership test against SENSITIVE_HEADERS. Marked
#[must_use].
use rtb_redact::is_sensitive_header;
assert!(is_sensitive_header("Authorization"));
assert!(is_sensitive_header("X-API-Key"));
assert!(!is_sensitive_header("content-type"));
Two behaviours worth knowing:
- Names longer than 128 bytes return
falsewithout being looked up. The lowercasing buffer is a fixed 128-byte array, and no name in the set is anywhere near that long, so an oversized name cannot be a member anyway. - Only ASCII letters are folded. A header name is ASCII in practice; a non-ASCII byte is passed through unchanged and simply will not match.
redact_header_value — mask a header value¶
Returns [redacted] for any non-empty value, and an empty String for an
empty one. Marked #[must_use].
use rtb_redact::redact_header_value;
assert_eq!(redact_header_value("Bearer abc123"), "[redacted]");
assert_eq!(redact_header_value(""), "");
It does not inspect the value, and it does not check the header name — it masks
unconditionally. Deciding whether to call it is the caller's job, which is what
is_sensitive_header is for. A whitespace-only value counts as non-empty and
becomes [redacted].
Note the asymmetry with string: redact_header_value allocates a new String
for every non-empty value, even one that would have survived string untouched.
The empty case returns String::new(), which allocates nothing.
Which function do I want?¶
| Situation | Use |
|---|---|
| A free-form line — log message, error text, telemetry attribute | string |
| The same, in a hot loop with a buffer to reuse | string_into |
| An HTTP header you are about to log | is_sensitive_header, then redact_header_value |
| Deciding whether your own header list is complete | read SENSITIVE_HEADERS, then add your own names on top |
What the API deliberately does not offer¶
- No
Errorhelper. Unlike the Go siblingredact.Error, there is no nil-safe error wrapper here; callstring(&err.to_string())yourself. - No
&[u8]entry point. Input is&str, so callers holding bytes convert first — and a byte slice that is not valid UTF-8 has no redacted form to return. - No custom patterns, no configuration, no runtime registration. See What rtb-redact does not do for the reasoning.