# compliantChatbot (/docs/recipes/compliant-chatbot)



`compliantChatbot` wraps `simpleChatbot` with per-profile
scrubbers. The `profile` field selects a curated bundle from
`@pleach/compliance/scrubbers`; the bundle (plus any
`extraScrubbers`) is applied to the user message string before
`ask()` forwards it to the runtime. When `supabase` is supplied,
the recipe also constructs an `@pleach/core` `EventLogWriter`
configured with the same scrubbers and plumbs it through
`host.modules.eventLogWriter` — so the persisted copy of every
event row is scrubbed at the substrate boundary too.

Best fit: **compliance-bound** or **regulated-industry**
deployments. Reach for this when HIPAA, GDPR, PCI-DSS, or SOC 2
requires the event log persisted or exported to be free of
unredacted PII.

## Quickstart [#quickstart]

Message-content scrubbing only (v0.2 baseline):

```ts
import { compliantChatbot } from "@pleach/recipes/compliance";

const bot = compliantChatbot({ profile: "hipaa" });
await bot.ask("patient called about prescription refill");
```

Opt in to EventLogWriter-boundary scrubbing by passing a
Supabase client:

```ts
import { createClient } from "@supabase/supabase-js";
import { compliantChatbot } from "@pleach/recipes/compliance";

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_KEY!,
);

const bot = compliantChatbot({
  profile: "hipaa",
  supabase,
});
```

## What it does [#what-it-does]

Profile → scrubber bundle mapping:

* `hipaa` → `SsnUsScrubber` + `UsDriverLicenseScrubber` + `CreditCardScrubber`
* `gdpr` → `SsnUsScrubber` + `CreditCardScrubber`
* `pci-dss` → `CreditCardScrubber`
* `soc2` → `SsnUsScrubber` + `CreditCardScrubber`

On each `ask(message)` the resolved bundle (plus any
`extraScrubbers`) runs against the message string before the
recipe delegates to `simpleChatbot.ask()`. Matches are replaced
with the scrubber's redaction marker.

When `supabase` is supplied, the recipe additionally constructs
an `EventLogWriter` with `onScrubberError: "fail-closed"` and
forwards it through `createPleachRuntime({host: {modules: {eventLogWriter}}})`. Every `write()` invocation runs the
`DefaultScrubberChain` against the per-event-type allowlist
(`content.delta`, `interrupt.edited`, `audit.recorded`,
`domain.ivy.job.*`) before the row lands in the durable buffer.

## Config reference [#config-reference]

```ts
type ComplianceProfile = "hipaa" | "gdpr" | "pci-dss" | "soc2";

interface ComplianceScrubber {
  readonly id: string;
  scrub(
    input: string,
    ctx?: {
      readonly eventType?: string;
      readonly fieldPath?: readonly string[];
      readonly tenantId?: string;
    },
  ): {
    readonly redacted: string;
    readonly matchCount: number;
    readonly matchedScrubberIds?: readonly string[];
  };
}

interface CompliantChatbotConfig extends SimpleChatbotConfig {
  profile: ComplianceProfile;
  /** Run AFTER the profile bundle (last-wins on overlap). */
  extraScrubbers?: readonly ComplianceScrubber[];
  /** Opt-in to EventLogWriter-boundary scrubbing. */
  supabase?: { from: (table: string) => any };
}
```

## Common gotchas [#common-gotchas]

* **Assistant responses are not scrubbed.** Scrubbing applies
  to the user message before the runtime sees it, and (with
  `supabase`) to event-log rows at write time. The model's
  reply itself is not post-processed. If you need to redact
  PII from generated text, run a scrubber pass in your UI
  layer or compose `ComplianceRuntime` from
  `@pleach/compliance` directly.
* **`@pleach/compliance` is an optional peer.** The recipe
  loads scrubbers via `require.resolve("@pleach/compliance")`
  at construction. Install `@pleach/compliance` alongside
  `@pleach/recipes` when using this recipe.
* **The recipe stays storage-agnostic.** It does NOT call
  `createClient(...)`. You construct the Supabase client and
  pass it; the recipe uses only the `.from(table)` chain
  entry and runtime-validates deeper access inside
  `@pleach/core`'s `EventLogWriter`.
* **Profile change requires a new instance.** Profiles
  resolve at construction; there is no `bot.setProfile()`.
* **`extraScrubbers` runs after the profile bundle.** Last-wins
  on overlap. If a custom scrubber must run first, prepend
  by wrapping it as the new bundle and dropping the profile —
  or open a feature request.

## See also [#see-also]

* [`@pleach/recipes` overview](/docs/recipes-pleach-recipes) —
  every recipe in one page.
* [`@pleach/compliance`](/docs/compliance) — the underlying
  scrubber library and `ComplianceRuntime`.
* [`Scrubbers`](/docs/scrubbers) — the redaction contract,
  per-event-type allowlist, and authoring guide.
* [`Regulated domain agent`](/docs/regulated-domain-agent) —
  full-system pattern with hash-chain attestation.
