> ## Documentation Index
> Fetch the complete documentation index at: https://docs.barndoor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Masking & Partial Redaction

> Replace a detected value with a placeholder, or keep the first or last few characters readable and mask the rest — credit-card style.

The **Mask** action replaces a detected value in place. By default the whole value becomes a typed placeholder such as `<SSN:MASKED>`. With a **mask format**, the value keeps its shape: the first or last few characters stay readable and everything else is replaced with a mask character, the way a receipt shows `XXXX XXXX XXXX 1234`.

Use masking when an agent or a downstream tool still needs to *recognise* a value without *seeing* it — matching a support ticket to an account by its last four digits, telling two commit SHAs apart by their prefix, or keeping a record id searchable.

<Note>
  Masking is irreversible. If you need to restore the original value later, use **Tokenize** instead. If nothing downstream needs the value at all, **Redact** or **Block** is the safer choice.
</Note>

## Mask versus Redact and Tokenize

| Action                 | Output                     | Reversible          | Value stays recognisable |
| ---------------------- | -------------------------- | ------------------- | ------------------------ |
| **Redact**             | `<…:REDACTED>` placeholder | No                  | No                       |
| **Mask** (no format)   | `<SSN:MASKED>`             | No                  | No                       |
| **Mask** (keep last 4) | `XXX-XX-6789`              | No                  | Yes                      |
| **Tokenize**           | `<SSN:…>` reversible token | Yes, via detokenize | No                       |

Mask is available on every profile type that reports it under **Supported Actions** on the Protection Profiles dashboard, and on both MCP policies and LLM policies.

## Choose Mask in a policy

Masking is configured per policy, in the **Profiles & Action** step of the MCP policy wizard or the LLM policy wizard.

1. Create or edit a policy ([MCP Policies](/how-tos/data-control-center/mcp-policies)).
2. In **Profiles & Action**, select the protection profiles to run.
3. Set **Action** to **Mask**. The **Mask format** panel appears under the action.

<img src="https://mintcdn.com/barndoor/yevux3y69kkMDW-p/images/data-control-center/platform-data-protection-mask-format--mcp-policy-wizard.png?fit=max&auto=format&n=yevux3y69kkMDW-p&q=85&s=2acea213051e94e70749bac73e8268b7" alt="MCP policy wizard on the Profiles & Action step with Mask selected" width="2080" height="1440" data-path="images/data-control-center/platform-data-protection-mask-format--mcp-policy-wizard.png" />

4. Choose a **Mask format** (see below), then save the policy.

The format applies to every value the policy's profiles detect. To mask different types differently — for example last-4 for SSNs and first-8 for order ids — create two policies with single-type profiles and order them by priority.

## Mask formats

### Full placeholder

The default. Every detected value becomes `<TYPE:MASKED>`, where `TYPE` is the detection type (`SSN`, `CREDIT_CARD`, and so on). This is the same behaviour as Redact with a different label, and it is what existing Mask policies do.

### Keep suffix

Keeps the **last N characters** readable and masks the rest. This is the card and SSN convention: the tail is enough to match a record, and the head is what makes the value sensitive.

<img src="https://mintcdn.com/barndoor/yevux3y69kkMDW-p/images/data-control-center/platform-data-protection-mask-format--keep-suffix.png?fit=max&auto=format&n=yevux3y69kkMDW-p&q=85&s=61f516a79a5ee95ed559bc1275e06f3c" alt="Mask format set to Keep suffix, keeping the last 4 characters" width="1792" height="1752" data-path="images/data-control-center/platform-data-protection-mask-format--keep-suffix.png" />

| Setting                    | Meaning                                                                                                                                            |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Keep last (characters)** | How many characters at the end stay readable, 1 to 16. Separators count.                                                                           |
| **Mask character**         | The character written over the hidden part. Default `X`. One character; not a digit, not a space, and not `<`, `>`, `"` or `\`.                    |
| **Keep separators**        | On by default. Dashes, spaces and dots inside the masked region are left in place so `111-22-6789` becomes `XXX-XX-6789` rather than `XXXXXX6789`. |

The **Preview** line shows the format applied to a sample card number, exactly as the runtime would render it.

### Keep prefix

Keeps the **first N characters** readable and masks the rest. Use it for identifiers whose leading part is the meaningful one: a commit SHA, an order or record id with a fixed prefix, an API key's vendor prefix.

<img src="https://mintcdn.com/barndoor/yevux3y69kkMDW-p/images/data-control-center/platform-data-protection-mask-format--keep-prefix.png?fit=max&auto=format&n=yevux3y69kkMDW-p&q=85&s=2b31f3bdc606b1318cbc04bba469f53b" alt="Mask format set to Keep prefix, keeping the first 8 characters with * as the mask character" width="1792" height="1752" data-path="images/data-control-center/platform-data-protection-mask-format--keep-prefix.png" />

The settings are the same as Keep suffix, with **Keep first (characters)** in place of Keep last.

## Examples

With **Keep separators** on and `X` as the mask character:

| Detected value                             | Format        | Result                                        |
| ------------------------------------------ | ------------- | --------------------------------------------- |
| `111-22-6789` (SSN)                        | Keep suffix 4 | `XXX-XX-6789`                                 |
| `111-22-6789` (SSN)                        | Keep prefix 7 | `111-22-XXXX`                                 |
| `4111 1111 1111 1234` (card)               | Keep suffix 4 | `XXXX XXXX XXXX 1234`                         |
| `9f86d081884c7d659a2f…` (40-character SHA) | Keep prefix 8 | `9f86d081XXXXXXXX…`                           |
| `ACCT-Zoë-00917` (custom type)             | Keep prefix 8 | `ACCT-Zoë-XXXXX`                              |
| `555-0142` (phone)                         | Keep prefix 8 | `<PHONE:MASKED>` — see the reveal floor below |

Characters are counted the way a person reads them, not as bytes: an accented letter or an emoji is one character.

The masked value is emitted bare, not wrapped in `<…>`. That keeps the residue usable in a follow-up tool call, and re-scanning already-masked output with the same policy leaves it unchanged.

## What is always fully masked

Two safeguards stop a partial format from revealing more than intended. Both fall back to the **full placeholder**, never to passing the value through.

### The reveal floor

A partial format is only applied when enough of the value is actually hidden: **at least 4 characters, and at least a third of the value's letters and digits**. Otherwise the whole value becomes `<TYPE:MASKED>`.

This lets the standard forms through (last-4 of a 9-digit SSN, first-6 and last-4 of a 16-digit card) and rejects the abuses (keep 12 of a 16-digit card, keep 8 of a 10-digit phone number). A format tuned for 40-character SHAs cannot leak most of a short phone number the same profile happens to catch.

### Free-text and payload-level types

Some detection types have no meaningful prefix or suffix to keep. Values of these types are always fully masked, whatever format the policy sets:

* **Names, places and addresses** — `PERSON`, `ORGANIZATION`, `LOCATION`, `ADDRESS`
* **Emails and dates of birth** — `EMAIL`, `DATE_OF_BIRTH`
* **Passwords** — every character is secret; there is no conventional visible tail
* **Payload-level verdicts** — prompt injection, guardrail interventions, code-execution and obfuscation findings

When a selected profile emits one of these types, the Mask format panel lists it under the settings so you know which of the policy's detections will get the placeholder instead.

<img src="https://mintcdn.com/barndoor/yevux3y69kkMDW-p/images/data-control-center/platform-data-protection-mask-format--free-text-types-always-fully-masked.png?fit=max&auto=format&n=yevux3y69kkMDW-p&q=85&s=91d944abcf8cd5567c92b71c340846b1" alt="Mask format panel noting that EMAIL, LOCATION and PERSON are always fully masked" width="1792" height="1784" data-path="images/data-control-center/platform-data-protection-mask-format--free-text-types-always-fully-masked.png" />

Everything pattern-shaped is eligible for partial masking: government and financial identifiers, phone numbers, every secret and API-key type, and all [custom detection types](/how-tos/data-control-center/protection-profiles#create-a-custom-detection-type).

## Custom patterns are full-match

A custom regex detection type always masks the **whole** match. A pattern such as `^(.{8}).*$` does not keep the first eight characters — capture groups are ignored, and the pattern matches the entire line, so the entire line is masked. To keep a prefix, write the pattern to match the identifier itself and set the policy's mask format to **Keep prefix**.

## Verify a mask policy

* **Test Workbench** — go to **Data Control Center → Test**, paste a sample payload, and run the policy. The output shows the masked form the runtime will produce.
* **Dry run** — turn on **Dry Run** on the policy to record detections without rewriting anything, then check **Detection Activity** to see what the policy would mask.
* **Audit** — each masked finding's audit record carries how many characters were left readable and whether the reveal floor forced the full placeholder. The revealed text itself is never recorded.

## API

The same setting is exposed on the enforcement-policy API as an optional `mask_format` object, valid only when `action` is `POLICY_ACTION_MASK`:

```json theme={null}
{
  "action": "POLICY_ACTION_MASK",
  "mask_format": {
    "keep": "SUFFIX",
    "keep_chars": 4,
    "mask_char": "X",
    "preserve_separators": true
  }
}
```

Omit it, or send `null`, for the full placeholder. Sending a format with any other action is rejected. The detection-type catalog reports `partial_mask_eligible` for each type.

<Note>
  The Terraform provider's `enforcement_policy` resource does not yet expose `mask_format`. Configure it through the console or the API until that binding ships.
</Note>
