Skip to main content
Every reply, streamed draft, and proactive send is passed through a scrubber before it leaves the process — on by default, no config.

Quick Start

1

Zero config — scrubbing is on by default

Registered gateway credentials and credential-shaped tokens are masked in every reply without any setup.
2

Opt out per bot

Disable the scrub for a single bot. Only do this when another layer upstream already guarantees no secret leaves.
3

Bring your own redactor (e.g. add PII)

Inject a custom scrubber that wraps the core primitive and adds extra rules.

What Gets Masked

Two things are masked before a reply leaves the process: every value registered via register_secret_for_redaction (all resolved gateway credentials, masked by exact value) plus any token matching a narrow credential-shape pattern.
Personal data (PII) is deliberately out of scope for the built-in scrub — the shape patterns stay narrow so ordinary text is never over-redacted. Layer PII on with a custom redactor (see below).

How It Works

The scrub runs after every MESSAGE_SENDING hook, so a hook cannot re-introduce a secret after your own logic runs.

Which Paths Are Covered

Every path that dispatches text to a chat user is scrubbed.
The reply seam runs the scrub last — after any MESSAGE_SENDING hook. Streaming edits bypass the reply seam, so both the rolling draft and the final answer are scrubbed in the streamer, before text-limit truncation, so a [REDACTED] mask is never cut across the length boundary.

Custom Redactor

Inject an OutboundRedactor to add a policy the core scrub does not cover — PII, a company-internal token pattern, and so on.
OutboundRedactor is a @runtime_checkable protocol with one method, redact(self, text: str) -> str. The injected redactor wins over the core primitive. It must return a str — any non-str return is ignored and the core redact_outbound runs instead.

Opt-Out

Turn the scrub off for a single bot by setting one attribute.
Only disable the outbound scrub if another guarantee upstream already owns secret redaction (for example, a PII policy that also masks credentials). Otherwise resolved secrets and credential-shaped tokens will be delivered verbatim.

Ordinary Text Is Unchanged

The scrub is additive — absent a leak, ordinary text passes through untouched.

Idempotency & Failure Modes

Scrubbing is idempotent — running it on already-scrubbed text is a no-op, so overlapping seams (finalize + reply hook) never double-mask. It is also best-effort: a scrubber exception is caught at every seam and the original text is delivered rather than dropped, so a bug in a custom redactor can never block delivery.

Relationship to the Inbound Gate

Inbound and outbound are symmetric. The Inbound Message Gate filters what reaches the agent through MESSAGE_RECEIVED; the outbound scrub filters what leaves the agent on the reply path. Together they bracket the agent on both sides.

Inbound Message Gate

Drop or redact incoming messages before the agent sees them.

Best Practices

The scrub is safe-by-default and additive — ordinary text is unchanged. Keep it on and opt out only when another layer already owns secret redaction.
The shape regex is deliberately narrow. For an unusual token format, register the value with register_secret_for_redaction(value) so it is masked by exact value even when the shape patterns miss it.
Don’t disable the core scrub to add PII masking — wrap it. Call redact_outbound(text) first inside your redact method, then apply your own rules.
For a tool that emits large or frequent output, scrub at the tool boundary with a guardrail — cheaper than scrubbing every reply. See Redact Tool Output.

Inbound Message Gate

The symmetric inbound filter on MESSAGE_RECEIVED.

Gateway Secret References

Load credentials from files, env vars, or secret managers.

Hook Events

MESSAGE_SENDING and the full hook event reference.

Redact Tool Output

Scrub secrets at the tool boundary before they reach a reply.