compliantChatbot
Chatbot with PII / PHI scrubbing at the message-content boundary and, when a Supabase client is supplied, at the durable EventLogWriter boundary. Four named profiles map to curated scrubber bundles from @pleach/compliance.
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
Message-content scrubbing only (v0.2 baseline):
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:
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
Profile → scrubber bundle mapping:
hipaa→SsnUsScrubber+UsDriverLicenseScrubber+CreditCardScrubbergdpr→SsnUsScrubber+CreditCardScrubberpci-dss→CreditCardScrubbersoc2→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
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
- 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 composeComplianceRuntimefrom@pleach/compliancedirectly. @pleach/complianceis an optional peer. The recipe loads scrubbers viarequire.resolve("@pleach/compliance")at construction. Install@pleach/compliancealongside@pleach/recipeswhen 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'sEventLogWriter. - Profile change requires a new instance. Profiles
resolve at construction; there is no
bot.setProfile(). extraScrubbersruns 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
@pleach/recipesoverview — every recipe in one page.@pleach/compliance— the underlying scrubber library andComplianceRuntime.Scrubbers— the redaction contract, per-event-type allowlist, and authoring guide.Regulated domain agent— full-system pattern with hash-chain attestation.
agentInstrumentation
Instrument any @pleach/core agent with bare lifecycle subscriptions — runtime.events.on(kind, ...) for stage, turn, recovery, retry, and model.called signals — then bridge model.called straight to a destination with observeSink({ destinations }). No recipe wrapper, no recorder threaded through call sites.
BYOK observability
How to drop @pleach/observe into an existing OpenAI / LangChain / hand-rolled agent loop and write one auditable row per LLM call to your own Postgres, Supabase, or OpenTelemetry collector. No @pleach/core commitment required.