Masking vs blocking in DLP: which to deploy first

A DLP layer found a secret in your agent’s outbound traffic. Now what? Two instincts compete. Block the request — the secret definitely doesn’t leave, but the agent’s task dies mid-flight. Or mask the secret and let the request through — the work continues, but you’re betting that redaction is enough. The choice between masking and blocking is a choice between failure modes, and it decides what you should deploy first.

What each mode does

Blocking drops the request entirely when a rule fires. The secret stays home. The cost: the agent’s operation fails — a code generation call aborted, an API call denied, possibly retried in a loop that burns tokens and time.

Masking rewrites the request in place: the matching substring is replaced before the body leaves. The request completes, the model gets the redacted context, and the workflow survives. The cost: you’re sending a modified version of your data, and if the mask is too aggressive, legitimate content gets mangled.

The failure modes, honestly compared

  • Blocking fails loudly. A blocked request is visible — it fails, and you can see it. But agents retry, and retry loops can amplify one bad rule into a cascade of wasted calls.
  • Masking fails quietly. A masked secret still travels in redacted form — the confidentiality question is answered, but the availability question is only deferred. And masking doesn’t prove the rule was right; it only proves the rule fired.
  • Both suffer false positives. The real difference is whether a false positive breaks a task or just annoys you.

That’s the availability-versus-confidentiality trade, and it’s the whole game. Blocking optimizes for “no secret leaves”; masking optimizes for “work continues”. You can’t have both at full strength on every request, because you can’t tell a false positive from a true hit at line speed.

Why masking comes first

Deploy in this order: log → mask → block.

Start with logging, where hits are counted but traffic passes untouched — you learn what your rules actually fire on without breaking anything. A week of log mode tells you which rules are noisy and which are worth trusting.

Then move to masking. It’s the first mode that protects secrets while keeping agents functional, and it’s the mode you can leave on permanently as the default: most hits are pattern matches, and masking the suspect substring is proportionate.

Blocking is the escalation for the cases where masking is not enough — known-value hits (exact secrets from your store), high-signal rules with zero noise, and destinations you never want data sent to at all. Blocking should be targeted, not the default.

The same logic applies to data at rest

Masking-first isn’t just for live traffic. Scrubbing stored data has the same shape: dry-run by default, review what would change, then apply. A scrubber that rewrites your database without a preview is blocking logic applied to storage, and it ends the same way — with a backup you hope you never need.

What this looks like in practice

A DLP layer with per-rule modes makes the rollout concrete: log mode while you calibrate, mask as the default for pattern hits, and a deny path for the specific cases that deserve it. Configuration that reloads without a restart and swaps rules atomically keeps the policy honest during the transition — a typo shouldn’t silently disable the filter.

trustless implements exactly this ladder: trustless serve exposes pattern_mode log/mask per rule, and trustless dlp scrub-db and scrub-text do two-layer redaction (known values plus patterns) with dry-run by default and --apply to commit — details in the command reference.

So, have you ever blocked a request that turned out to be a false positive — and if so, was the retry loop worse than the leak would have been?