Redact secrets
redact(value, options?) deep-copies a value with any property whose key matches a sensitive fragment replaced by a placeholder. It is key-driven, not value-driven — it never guesses that a bare string is a secret; it only redacts when the key looks sensitive (authorization, apiKey, cookie, password, token, …). One shared redaction policy backs three trust-boundary surfaces — VCR cassettes, Panoptic content capture, and the error / cause serializer — instead of three divergent guards.
This is not ai.guardrail. Guardrails are agent middleware that inspect prompt / output / tool content at runtime and emit allow / redact / block / flag verdicts. redact() is a plain synchronous utility for scrubbing structured payloads (recorded requests, error causes, captured traces) before they are stored or exported.
redact(value, options?)
Section titled “redact(value, options?)”import { redact } from "@warlock.js/ai";
const safe = redact({ url: "https://api.openai.com/v1/chat/completions", headers: { "content-type": "application/json", authorization: "Bearer sk-live-abc123", // key is sensitive → redacted "x-api-key": "secret-value", // redacted }, body: { model: "gpt-4o", apiKey: "leaked" }, // nested apiKey → redacted});
// {// url: "https://api.openai.com/v1/chat/completions", // untouched (key not sensitive)// headers: {// "content-type": "application/json",// authorization: "[redacted]",// "x-api-key": "[redacted]",// },// body: { model: "gpt-4o", apiKey: "[redacted]" },// }It returns a new value of the same type T — the input is never mutated. Primitives pass straight through, arrays are walked element-wise, and plain objects are rebuilt key by key.
Options
Section titled “Options”| Option | Type | Default | What it does |
|---|---|---|---|
keys | string[] | — | Extra key fragments to redact, merged with DEFAULT_SENSITIVE_KEYS. Matched case-insensitively as substrings of each object key. |
placeholder | string | "[redacted]" | Replacement value substituted for a redacted property (and for collapsed circular / over-deep branches). |
maxDepth | number | 8 | Maximum recursion depth before a branch collapses to the placeholder. |
Matching is substring, case-insensitive
Section titled “Matching is substring, case-insensitive”A key is sensitive when its lower-cased form contains any fragment. So Authorization, authorizationHeader, and X-Authorization all match the authorization fragment. The built-in set:
import { DEFAULT_SENSITIVE_KEYS } from "@warlock.js/ai";
// "authorization", "x-api-key", "api-key", "apikey", "cookie", "set-cookie",// "password", "passwd", "secret", "token", "access_token", "refresh_token",// "client_secret", "private_key", "session"Add your own fragments without losing the defaults:
const safe = redact(payload, { keys: ["ssn", "card_number"], placeholder: "***" });Cycles and depth
Section titled “Cycles and depth”Circular references and trees deeper than maxDepth collapse to the placeholder rather than throwing or looping:
const node: any = { name: "root" };node.self = node; // circularredact(node); // { name: "root", self: "[redacted]" }redactHeaders(headers, placeholder?)
Section titled “redactHeaders(headers, placeholder?)”Strip sensitive HTTP headers from a Headers instance or a plain record, returning a redacted plain object. Header names are matched case-insensitively against SENSITIVE_HEADERS (authorization, x-api-key, api-key, cookie, set-cookie, proxy-authorization).
import { redactHeaders } from "@warlock.js/ai";
redactHeaders(new Headers({ authorization: "Bearer x", "content-type": "application/json" }));// { authorization: "[redacted]", "content-type": "application/json" }placeholder defaults to "[redacted]"; pass a second argument to override. A missing / undefined argument returns {}.
scrubSecrets(text)
Section titled “scrubSecrets(text)”Where redact() is key-driven and can’t see a secret embedded in a string, scrubSecrets() scrubs secrets that appear in free-form text — error messages, stack traces, exported log lines. It replaces each known secret shape with [redacted] while keeping the surrounding context.
import { scrubSecrets } from "@warlock.js/ai";
scrubSecrets("request failed: Authorization: Bearer sk-live-abc1234567890123");// "request failed: Authorization: Bearer [redacted]"Built-in patterns cover Bearer <token>, authorization / x-api-key / api-key / cookie key-value pairs, OpenAI-style sk-… keys, Slack xox… tokens, GitHub ghp_… / gho_… tokens, and AWS AKIA… access-key ids.
redactError(error, options?)
Section titled “redactError(error, options?)”Produce a serialized, secret-free view of an error — safe to log, persist, or ship across a boundary.
import { redactError } from "@warlock.js/ai";
const safe = redactError(providerError);// {// name: "ProviderAuthError",// message: "...",// code: "PROVIDER_AUTH",// cause: { headers: { authorization: "[redacted]", ... } }, // deep-redacted// }It returns a RedactedError:
type RedactedError = { name: string; message: string; code?: string; cause?: unknown; // deep-redacted via redact() stack?: string; // only when includeStack: true};Behavior:
stackis omitted by default — a stack can embed local paths, endpoints, and tokens. PassincludeStack: trueonly for a trusted local sink.- The retained
causeis deep-redacted viaredact(), so a raw provider SDK error carryingAuthorization/x-api-keyoncause.headersis sanitized. - Any remaining
RedactOptions(keys,placeholder,maxDepth) are forwarded to thecauseredaction. - A non-object error (string, number,
null) returns{ name: "Error", message: String(error) }.
redactError(err, { includeStack: true, keys: ["ssn"], placeholder: "***" });Related
Section titled “Related”- Guardrails — runtime content detectors (
ai.guardrail); not the same as this key-driven utility. - Handle errors — the typed
AIErrorhierarchy whosecauseredactErrorsanitizes. - Record / replay testing — VCR cassettes redact recorded requests with this policy.
- What Panoptic traces — captured content runs through the same redaction.
- Log AI calls — channel-level redaction for logs (a separate
@warlock.js/loggerfeature). - API reference — full export index.