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). |
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¶
Where to go next¶
The documentation follows the Diátaxis framework:
- Getting started — a learning-oriented walkthrough: redact a secret-bearing string, wrap an error, and learn where to call it.
- How-to guides — task-oriented recipes:
- Explanation — understanding-oriented background:
- Reference — the full API, with runnable examples, lives on pkg.go.dev.
Not a silver bullet. Pattern catalogues never reach 100 % recall. A credential in a bespoke, non-standard format will slip through — so anywhere you handle such inputs, redact upstream too. The rule catalogue sets out exactly what is and is not caught.