Solutions

83 kinds of identifier, found before the request leaves.

Card numbers, national identifiers and keys are found in the request and rewritten on the same bytes before the provider sees them. The offset that finds a value is the offset that rewrites it.

what it reads
the request body and the response stream, in the process holding the connection
the operators
replace, redact, mask, hash, placeholder, pseudonym, fpe
what a stream waits for
the longest span a selected detector could still be growing into
turned back for
the MCP servers and agents you dial, by restore list

A prompt is assembled at runtime out of a conversation, a record lookup and a tool result, so what personal data leaves is a property of each request. One process holds the connection while that request is inspected, rewritten and forwarded, and the address that finds a value is the address that rewrites it.

The pii detector finds 83 kinds of identifier with nothing configured: card and bank numbers, national identifiers, passports, driving licences, tax, health and vehicle numbers, phone numbers, email addresses and dates. Names are not among them, because a name has no shape a pattern can describe; the nlp detector and a model find those. 51 of the 83 carry a checksum, so a match the arithmetic rejects is dropped instead of redacted.

9 are recognizers Gate adds where Presidio has none, and they are Gulf: Saudi national ID, unified number and VAT number, UAE Emirates ID, passport and TRN, Kuwait civil ID, Qatar ID, and Oman's IBAN. The same detector finds 19 kinds of API key and private key material, annotated credential/… because a key is not personal data. Every type, its patterns and its checksum are listed, and credentials have their own page.

Coverage does not decide what happens to a value. A rule decides which of those types it acts on, a validating rule leaves an identifier the checksum could not confirm, and the sections below say what each operator costs the request it rewrites.

What the client sent

Please cancel my card 4095260993934932, my id is 1044880134, and mail me at aarav@example.com.

pii · fpe · placeholder

What the provider received

Please cancel my card 6880989289285872, my id is <SA_NATIONAL_ID_1>, and mail me at <EMAIL_ADDRESS_1>.

The card is still sixteen digits and still passes Luhn. Only this gateway's key turns it back.

403

sa-ndmo-personal-data-abroad-needs-approval guardrail rule

personal data is processed outside the Kingdom only through a provider whose metadata declares the written approval as transfer_basis

One pass, one address

A detector returns a finding as byte offsets into the text as sent. The operator rewrites those bytes. Nothing is re-parsed between the two, so a rewrite cannot land on the wrong characters because a second pass disagreed about where the value started. Everything the rule did not select is forwarded byte for byte.

So a redaction cannot miss a field the detector found, and cannot damage one it did not.

Where this sits

A DLP programme covers endpoints, mail and storage. Gate covers the request, at the last hop where it is still yours to change.

Responses are inspected the same way. A streamed response is released as it arrives, held back only by the longest span a selected detector could still be growing into, and each type's longest span is on the coverage page. The commit horizon explains how a stream is released.

Every operator breaks a tool call

The operator is chosen per rule. The choice is a trade between what the model can still work with and what a tool downstream can still work with.

operatorwhat the provider receiveswhat a tool receives
replace<PERSON>a type name, and an error
redactthe span is removedan empty argument, and an error
mask************asterisks, and an error
hash64 hex charactersa hash, and an error
placeholder<PERSON_1>a placeholder, and an error
pseudonymGrace Hughesa name. It may find the wrong customer
fpe4532-7712-0483-1965a valid card. It may find the wrong account

Realism is the purpose of pseudonym and fpe, and it is the one place they are worse than the operators they improve on. A stand-in that reads as ordinary text reads that way to a tool as well. The first five operators break a tool call loudly: the argument is empty or malformed, the tool errors, and something in the trace says so.

Hand a tool Grace Hughes or a well-formed card number instead and there is nothing to notice. lookup_customer(name="Grace Hughes") may find a real customer, the wrong one, and return 200.

Where a tool consumes the value and cannot be told the real one, use placeholder. It produces worse text for the model and a failure somebody notices, which on a path ending in a lookup is the better trade. Refusing the call outright is the other answer, and a rule's when can read the server and the tool to say where.

Format-preserving encryption

fpe encrypts a span into another span of the same shape with FF1, from NIST SP 800-38G. The implementation is written against the specification, without a dependency, and it is checked against every sample in NIST's own worked examples. FF3 is not offered: it was withdrawn after Durak and Vaudenay broke it.

A replacement that fails Luhn is rejected by anything that checks it. A replacement that passes Luhn but begins 59 is worse, because no issuer uses that prefix: no recognizer matches it, no later scan finds it, no rule meant to see it fires, and nothing can turn it back. The cycle walk keeps drawing until it has one that survives both.

FF1 is refused below a million possible values, the floor the draft revision of SP 800-38G raised it to. Four decimal digits is ten thousand values, and an attacker holding the ciphertexts has the codebook, so the operator declines. Every way of declining ends at a numbered placeholder. Declining to encrypt is never permission to pass the value through.

Turning it back

A value fpe rewrote is turned back for the systems you trust. An MCP server's or an A2A agent's restore list names the entity types it may receive in the clear, which is the declaration that it is inside your boundary. It applies only at a hop Gate dials, because that is the only destination anyone can promise anything about, and only in the tool arguments or message parts going there.

Names are the exception. A pseudonym is a many-to-one fold followed by a keyed pick from a word list, and neither half inverts. The forward map takes unbounded text to a short plausible name, so anything that inverts it has stored the original somewhere. A PERSON reaching a tool that wanted the real one keeps failing quietly, and a server with no restore list receives stand-ins by design.

What finds it

The pattern and model tiers run in the process that holds the connection, and a rule chooses which detectors it reads. Patterns carry checksums and context words, so a nine-digit run becomes a routing number when it satisfies the ABA check and the words around it agree. Models are pinned ONNX exports run through the gateway's own tokenizer binding. type: remote speaks presidio-analyzer's /analyze, so an existing Presidio deployment is a valid detector with no adapter to write.

Gate ships some models and pins other people's, and either can be a Hugging Face commit you name yourself. Both are measured on the build that runs them, against the traffic a deployment carries, before they go in. The research reports are those measurements, and choosing a detector is how the numbers in them were taken.

Precision and recall on a public set

These are the pattern tier alone, with no model, over the first 10,000 documents of Nemotron-PII at threshold 0.55.

typePRF1
EMAIL_ADDRESS0.9980.9970.998
MAC_ADDRESS0.9981.0000.999
US_SSN1.0000.9610.980

Scored against the set as published, card recall is 0.114, because 89% of that set's card numbers fail Luhn and the recognizer correctly refuses them. Routing numbers are the same story with the ABA checksum. On the 8,458 documents whose numbers are all valid, recall is 1.000 for both, at precision 0.75 for cards and 0.81 for routing numbers. Any model trained on that set learns that invalid card numbers are cards. A checksum tier does not.

For Arabic names, the XLM-R model the report recommends scores 0.748 exact and 0.934 overlap-allowed F1 on a Gulf chat set built for the hazards no public corpus carries. The clitic slice scores 0.246 exact, because every Arabic NER corpus annotates a prefixed و, ل or ب as part of the name and a redactor wants it outside the span. The report says what that costs.

Threat model

The asset is a customer's identifiers: a card number, a name, a national ID, an address. The text that carries them is written by your users, by your own records when a lookup is pasted into the prompt, and by a tool's reply. It arrives in the request body and comes back in the response stream.

The control is a detector that returns byte addresses and an operator that rewrites them, in one process, before the provider sees the request. The limit is the shapes the detectors know: paraphrase, an unusual encoding or a description of a secret pass through. This stops accidents and produces evidence. An adversary with time is a different problem. The entry this section expands sits with the others in threats and limits.

Where the pass ends

  • It does not find AI it is not in front of. What comes through Gate is seen, and drift on a key Gate issued is caught by the coverage audit. Traffic on a key nobody told it about is neither.
  • It does not fold coreference. Values are folded on case, whitespace, surrounding punctuation, a trailing possessive and a leading honorific. A pronoun or a description standing in for a name earlier in the conversation is a different string and is treated as one.
  • Arabic clitic boundaries are open. On names carrying a prefixed clitic the span comes back one letter too long, so the clitic is rewritten with the name and the sentence loses the word it belonged to.

The posts behind this

Start with Redact PII from model output, or read the measurements.

On the platform

More risks