October 30, 2026

Your PII redactor does not know what a name is

There is no pattern for a person's name that does not also match ordinary prose. So redact will not remove one. Priya Raghunathan reaches your model provider unchanged, and guard_flags comes back empty, because nothing matched.

Thin amber linework on charcoal: a filter mesh catching several structured tokens while an unstructured shape passes through.

I want to write this down plainly because "PII redaction" is a phrase that gets typed into a compliance questionnaire and ticked, and the gap between what the phrase suggests and what any pattern matcher can actually do is where a real obligation goes unmet.

What is actually detected

Pattern matching, not a model — fast, deterministic, free. It finds identifiers, not people. This is the whole list:

FlagMatches
pii:EMAILemail addresses
pii:CARD13–16 digit payment card numbers, spaced or hyphenated
pii:PHONEinternational (+44 7700 900123), national trunk (07700 900123), US (555-123-4567)
pii:SSNUS Social Security numbers
pii:NINOUK National Insurance numbers
pii:IPIPv4 addresses

Anything on that list we do not match is a bug worth reporting. Anything not on it, we do not claim to find.

Not detected: names. Street addresses. Dates of birth. Free-text notes about a person — which, on a product where users describe situations involving other people, is most of the personal data in the system.

So redact reduces what reaches your provider. It does not guarantee that nothing identifying does.

Which means the control has to match the obligation

If your obligation is "no personal data leaves our infrastructure", the control that meets it is a self-hosted or region-pinned model, configured per project under Models. Redaction is a second layer on top of that, not the first one.

If your obligation is "card numbers and national identifiers must not reach a third-party model", redaction meets it, because those are exactly the things a pattern can find with certainty.

Those are different obligations and they have different answers, and the failure I have watched happen is a team meeting the first with a control that only delivers the second — then finding out during a customer's security review rather than during their own design.

Why it is off by default

This surprises people, so: on a "talk to your own data" product, the end-user is sharing their own information on purpose. That is the feature.

A user uploading their own medical records to ask questions about them does not want their date of birth redacted before the model reads it. A support agent pasting a customer's order confirmation needs the order confirmation. Redaction that fires by default breaks the product for the majority case in service of a minority obligation.

Three modes:

ModeBehaviour
offthe model receives the text unchanged (default)
detectflagged in guard_flags for audit, the model still gets the raw text
redactreplaced before the model or provider sees it

detect is the mode most people actually want and the one nobody picks first. You get the audit trail — which turns carried card numbers, how often, from which subjects — without breaking anything. It is the right place to start if you are trying to find out whether you have a problem, rather than acting on one you already know about.

Choose redact only if you must keep identifiers away from your model provider, and accept the consequence: the model can no longer use them to answer. A user asking "did my payment on card ending 4419 go through" is asking a question that redact has just made unanswerable.

The audit side is the part worth setting up

Every turn is recorded with the guardrail flags that fired. So is every change to the corpus and every search of it — and the rows are shaped so that the two questions an audit exists to answer are answerable from the rows themselves:

QuestionThe rows
When did this document arrive, and who put it there?document_upload
Who removed it, and what was it?document_delete — written before the row goes, so it still describes what went
Who changed who may see it?document_update, with previous and new value of each changed field
Who has seen this document?retrieve and chat, both carrying metadata.document_ids

A document_update row is written only when a field actually changed. Setting a document to what it already was is not an event, and an audit log full of non-events is one nobody reads.

ts
const rows = await ai.audit.list({ document: doc.id, limit: 100 });

That finds both kinds — the rows that name the document and the rows that merely saw it.

Erasure

ts
await ai.audit.forget("acme:fin:ana");
// { documents_deleted, sessions_deleted, vectors_purged, objects_deleted }

One call removes a subject's documents, sessions and vectors. It returns what it reached, rather than a bare 204, because "we deleted your data" is a claim somebody may have to substantiate.

Worth knowing where erasure interacts with the rest: memories are scoped to subject, so they go with it. A self-visibility document is unlistable by anyone but its uploader — which means an operator's own count of the corpus is short by however many exist. with_total answers hidden alongside total for an admin, so you can know the number without being able to read them. A count is not a read: it names no filename, no owner and no text.

That last one is the pattern I would take away from this whole area. The useful question is rarely "may this person see this data". It is "what is the smallest true thing I can tell them", and it is usually a number.

Read next