redact¶
A small, zero-dependency helper that strips credential-like content from free-form strings at the boundary between your process and untrusted observability surfaces — telemetry vendors, log aggregators, metric stores.
redact is the same sanitiser behind go-tool-base's
telemetry and logging paths, extracted so any project can scrub secrets out of
error messages, command arguments, and HTTP header values on their way out —
without pulling in a framework.
The boundary-redaction idea¶
Error messages, command-line flags, and HTTP headers carry credentials by
accident all the time: an HTTP client wraps a URL with an embedded token, a
--api-key=sk-abc123 flag lands in os.Args, an OTLP export error quotes an
Authorization header. The instant that content reaches a third party it is
outside your control — replicated, indexed, and retained longer than you
intended.
The defence is to redact at the boundary: run untrusted strings through
String in-process, immediately before they are shipped to a
surface where a leaked credential would be harmful. Redact on the way out —
not on local, host-only debug logs that never leave the machine and may need the
raw content.
The zero-dependency promise¶
The import graph is deliberately empty. The package is pure standard library
(regexp and strings), so nothing external enters your build when you
depend on redact — the only third-party module in go.mod is a test-only
assertion library that never ships in your binary. A depfootprint_test.go
guard fails the build if a framework, cloud SDK, or telemetry dependency ever
creeps into the graph.
Every pattern is compiled once at package init and uses Go's RE2 engine (no backtracking), so matching is linear in the length of the input — there is no ReDoS exposure even on adversarial strings.
Quick start¶
package main
import (
"fmt"
"gitlab.com/phpboyscout/go/redact"
)
func main() {
raw := "request to https://admin:hunter2@api.example.com/v1?api_key=sk-abc123XYZ failed"
fmt.Println(redact.String(raw))
// request to https://<redacted>@api.example.com/v1?api_key=*** failed
}
The URL userinfo, the api_key query parameter, and the sk- token are each
masked, while the shape of the string — enough to debug with — survives.
The API¶
The public surface is four symbols. Nothing else is exported.
| Symbol | Purpose |
|---|---|
func String(s string) string |
Redact a free-form string; idempotent, never panics, returns the input unchanged when nothing matches. |
func Error(err error) string |
String(err.Error()), but nil-safe — returns "" for a nil error. |
var SensitiveHeaderKeys []string |
The canonical list of HTTP header names whose values should be redacted before logging. |
func IsSensitiveHeaderKey(name string) bool |
Reports whether a header name looks credential-bearing (case-insensitive). |
There is nothing else to configure — no options struct, no constructor, no interface. The API reference covers each symbol's edge cases and guarantees.
Design¶
- Zero dependencies. Pure standard library; a
depfootprint_test.goguard enforces it. - Boundary redaction. Sanitise where data leaves the host, not everywhere.
- Conservative by default. The opaque-token fallback requires ≥ 41 characters, so it never false-positives on UUIDs, MD5, or SHA-1 hashes.
- Linear-time and safe. RE2 patterns, no backtracking;
Stringis idempotent and provably non-panicking (fuzz-guaranteed).
Install¶
What redact does not do¶
Pattern catalogues never reach 100 % recall, and this one is deliberately conservative. The headline limits:
- You cannot add a pattern. There is no configuration surface at all — no options, no config file, no environment variable.
Stringdoes not mask arbitrary HTTP header values. It knowsAuthorization:and nothing else;X-API-Key: …passes through untouched.- Bespoke and short secrets slip through. The catch-all fallback needs 41 characters, and every provider prefix has a hard minimum length.
- Patterns are ASCII-only, and redaction is one-way — there is nothing to reverse and no record of what was replaced.
What redact does not do states the full boundary, question by question.
Where to go next¶
The documentation follows the Diátaxis framework:
- Tutorial — learning-oriented:
- Getting started — redact a secret-bearing string, wrap an error, learn where to call it, and watch it miss one.
- How-to guides — task-oriented recipes:
- Reference — information-oriented, and normative:
- API reference — all four symbols, their edge cases and guarantees
- Redaction pattern reference — every rule, its regex, and what it replaces
- Sensitive header reference — the curated list and the wider fuzzy match
- Generated API docs and runnable examples live on pkg.go.dev
- Explanation — understanding-oriented background:
Further reading¶
The blog carries a curated route through this subject: Building a command-line tool in Go 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.