Prompt Data Guard

Before a message goes to an AI chat, says exactly what personal data, secrets or confidential documents are in it and where they'll go.

StopsHelps keep a customer list out of an AI chat

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/prompt-data-guard.json

The files land in your project. They're yours to edit.

Usage

prompt-data-guard-usage.tsx
import { PromptDataGuard, restoreStandIns, type PromptAttachment } from "@/components/ui/prompt-data-guard"

<PromptDataGuard
  value={text}
  onValueChange={setText}
  attachments={files}
  onRemoveAttachment={(id) => setFiles((f) => f.filter((x) => x.id !== id))}
  destination={{ name: "OpenAI", retention: "up to 30 days" }}
  placeholder="Ask anything"
  onSend={async ({ mapping, protected: safe }) => {
    await new Promise((r) => setTimeout(r, 500))
    // The model only ever saw stand-ins; the real names come back on this device.
    const fromModel = "Top churn risk: <NAME_3> (‹EMAIL_3›), last seen 90 days ago. Draft: \"Hi ‹NAME_3›, we miss you…\""
    setReply(safe ? restoreStandIns(fromModel, mapping) : "Top churn risk: Meera Rao, last seen 90 days ago.")
    setText("")
  }}
/>

What it catches

  • Pasted tables with personal data, counted by row
  • Names in name columns and their other mentions
  • Emails, phones, card numbers, Aadhaar and PAN
  • API keys and other secrets (via detectSecrets)
  • CONFIDENTIAL markings in attachments

Props

PromptDataGuard props
PropTypeDefault
value

Controlled message text.

string—
defaultValue

Initial message text when uncontrolled.

string""
onValueChange

Called with the text on every change.

(value: string) => void—
attachments

Files attached to the message.

PromptAttachment[]NO_ATTACHMENTS
onRemoveAttachment

Called with an attachment's id to remove it.

(id: string) => void—
destination

Who receives the message and for how long they keep it, e.g. { name: "OpenAI", retention: "30 days" }.

{ name: string; retention?: string; training?: boolean }{ name: "the AI provider" }
onSend

Sends the message. May return a Promise: the button shows a pending state and can't fire twice.

(payload: PromptSendPayload) => void | Promise<void>—
detectSecrets

Secret finder. Defaults to secret-paste-guard's; pass false to skip secrets.

SecretDetector | falsedefaultSecrets
detectNames

Optional name finder for free text (an NER model, your CRM's contact list). Names in table name columns are always caught.

NameDetector—
className

Extra classes for the root.

string—
textareaClassName

Extra classes for the textarea.

string—
messages

Override any visible string, including per-kind and chip texts.

Partial<Omit<Messages, "kind" | "chip">> & { kind?: Partial<Record<DataKind, string>>; chip?: Partial<Record<DataKind, string>> }—

* required. Also accepts the native props of <textarea> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible label; textarea is described by the readback when risky
  • Remove-attachment buttons have aria-labels naming the file
  • Focus returns to the field after changes; animations are skipped under reduced motion

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Apply DLP and redaction on the server or gateway before data reaches the model provider.

  • Names in free text need your detectNames (an NER model or CRM list)
  • Destination and retention text come from your destination prop
  • Detection is pattern-based; it can miss data or flag harmless text

What it stops

Helps keep a customer list out of an AI chat

Also known as

ai chat · chat composer · prompt box · data loss prevention · dlp · pseudonymize · redact · openai

Field type

Dependencies

Pairs well with