Skip to main content
A query runs against real production data and returns real production data. Data Masking sits on the way back: the Sidecar decodes the response as it streams, finds sensitive values, and rewrites them before the client ever sees them. Nothing is copied, staged or transformed at rest. The masking happens in memory, in real time, at the protocol layer — so it works the same for psql, for an ORM, for a dashboard and for an agent.
Masking runs on responses only. Requests are never rewritten: changing the statement the upstream executes is a correctness change wearing a privacy label.
Free tier: one Data Masking rule and one Guardrail per Sidecar are free, forever. Running more than one rule per feature, or managing rules centrally across Sidecars, requires Enterprise.

Configuration

One block. mask says what to rewrite, and a mask block carrying rules masks — there is no second switch to set.
config.yaml
entities is a list, even for one type, and one rule can name several:
Detection needs no configuration of its own: omit pii and every supported entity is enabled. Add the section only to narrow the set, which also makes it exhaustive — a mask rule naming a type outside the list is then refused at startup rather than silently masking nothing.
config.yaml
The same query now comes back rewritten:
Breadth has a cost. US_SSN carries no checksum, so nine digits in a legal range is a valid one as far as any detector can tell, and a mask rule naming it rewrites ordinary numeric columns:
Both are reported as US_SSN. Prefer a columns rule for a column you can name, and reach for a narrowing pii.entities list when a lane’s data makes a type genuinely ambiguous.

Rule fields

Strategies

hash is the one worth knowing about: equal inputs give equal outputs, so a masked column still works as a join key. An analyst can group by customer without ever seeing a customer.

Entity rules and column rules

Two ways to name what gets masked, and they fail in opposite directions. Entity rules mask by detection. They work anywhere a value appears, including inside an opaque HTTP body where the protocol names nothing — and they miss whatever the detector does not recognize. Column rules mask by position. They cannot miss, because they never guess — and they only work where the protocol names its values, which means result sets, not payloads.
Use column rules for the columns you know, and entity rules for everything else.

Inheritance: a listener’s mask block replaces the defaults

This is the single most common way a working config quietly stops masking:
config.yaml
A listener’s mask block replaces the top-level list rather than extending it. Adding one column rule drops every inherited entity rule, so list those again alongside it. This is deliberate: a rule owns an entity type, and two concatenated lists would leave two rules competing for one entity. guardrails.rules behaves the opposite way — it concatenates.

Verify what actually resolved

The admin API reports the resolved state per listener, which is the only place the merge above is visible:
If a value comes back unmasked, the detector probably refused it rather than missed it — obvious test fixtures like 123-45-6789 are deliberately not reported. Add a columns rule, or check whether a pii.entities list is narrowing detection past the type you expected.

Looking for masking on the Hoop Gateway instead? That is a different implementation, configured in the web app with DLP providers and per-resource roles. See Live Data Masking.

Next

Guardrails

Control what reaches the resource, not just what comes back.

Config File Reference

Every field, the full entity list, and what startup refuses.