Data loss prevention

Detect card numbers, secrets, personal data and your own code in prompts before they reach a model, and record, mask, re-route or block them.

4 min read Updated View as Markdown Report a problem

Data loss prevention (DLP) scans prompts on their way to a model and acts on sensitive data it finds. Nothing is scanned until you turn a data type on, and every detector runs inside the ACOG deployment. Detection never sends the values it finds anywhere else, and its records never contain them (but see Stored conversations and DLP).

Open Admin → Data loss prevention. Viewing it requires View invoices and billing status; changing it requires Manage data-loss-prevention rules and connect source code (admin:security).

What it detects

Detection runs in three layers.

LayerDetectsDefault action when turned on
1. Exact dataCredit card numbers, US Social Security numbers, bank routing numbers, IBANs, live secrets and API keys. Checked by format and checksum, so it is the most precise layer.Block
2. Free textPeople’s names, email addresses, phone numbers, mailing addresses, IP addresses, medical and health terms, and your own custom dictionary of termsAudit
3. Protected sourcesText that matches code or documents in repositories you connectAudit

Custom dictionary

Add words or phrases specific to your organization, such as project code names. ACOG stores a one-way hash of each term, not the text. Terms are up to 128 characters, with options for whole-word and case-sensitive matching.

Protected sources

Connect a GitHub or GitLab repository and ACOG indexes it as fingerprints. A prompt that contains text from the repository is detected even when it has been edited slightly. The repository content itself is not kept.

Actions

ActionEffect
AuditThe prompt goes through unchanged. ACOG records that it saw the data type, never the value.
Tokenize(Exact data and free text, set through the API or a policy rule) The value is replaced with a deterministic token before the prompt leaves, and the real value is restored in the reply, so the workflow still works and the model never sees it.
MaskThe value is replaced with a placeholder before the prompt leaves. The model never sees it.
Route(Protected sources and policy rules) The request may only be served by platforms you approve, for example your own cloud account.
BlockThe request is refused and never reaches the model.

Each detector has a confidence threshold from 50 to 100% (60 to 100% for protected sources). The recommended starting points are 85 for exact data, 50 for free text and 85 for protected sources.

Policy rules

Policy rules apply different actions for one team, one person or one application, on top of the data types you turned on. Precedence is organization, then team, then the person or application. A rule marked Off is not applied.

What a blocked request gets

{
  "error": {
    "type": "dlp_blocked",
    "message": "…",
    "category": "credit_card",
    "reason": "layer1_baseline",
    "decision_id": "…"
  }
}

The status is 403. Give the decision_id to an admin to find the event. reason is one of layer1_baseline, layer2_detection, corpus_match, no_compliant_route, dlp_unavailable or input_too_large_to_scan.

If the scanner is unavailable

Exact-data checks run inside the gateway and are always available. Free-text and protected-source detection run in a separate service. If it cannot be reached, the setting If the scanner is unavailable decides:

ChoiceEffect
Block the request (default, recommended)The request is refused with 403 dlp_blocked, reason dlp_unavailable
Let the request throughThe request continues without those layers

What is scanned

Scanned
Prompts on the compatible endpoints and native routesYes
Files sent inline in a promptAs text
Requests whose body is not JSONExact-data types only
Embeddings requestsNo
Model repliesIn a successful non-streamed reply, tokenized values are restored, and any exact-data value (card, Social Security number, routing number, IBAN, secret) whose type is set to Mask or Block is masked before the reply reaches you. Free-text types are not masked in replies. In a streamed reply, tokenized values are restored; masking is not applied.

Seeing what was found

The Overview tab shows findings by data type and outcome. Blocks and masks are delivered as the governance.dlp_blocked webhook event. A detection record holds the category, its confidence, a reference ID and the position in the text, never the value itself.

Stored conversations and DLP

Store prompts for review (on by default, under Admin → Gateway settings) keeps each conversation as it was sent to the model, which is after DLP has acted:

Action for the data typeIn the stored conversation
BlockNothing: the request never reached the model
MaskThe placeholder, not the value
TokenizeThe token, not the value
AuditThe value, because Audit lets the prompt through unchanged

The same applies to prompt and completion text sent to your own Langfuse project. In the stored and exported reply, only exact-data types set to Mask or Block are masked; a free-text value the model writes into its reply is kept as written. If a data type must never be kept, set it to Mask or Block and, for free-text types, turn off prompt storage and Langfuse prompt and completion capture (see Observability). See Activity and audit log.

Self-hosted installations

Names and medical terms need a named-entity model. The chart deploys one by default; the DLP service reports ner_ready when it is loaded. See Configuration.