Skip to content

Transform reference

Every transform available to a masking rule. This page is generated from engine/internal/masking/transform.go, so a transform that exists and is not here fails the build.

The unique column matters more than it looks. A transform that does not preserve uniqueness cannot be used on a column with a unique constraint: the masked values collide and the update fails partway. af mask plan catches that before anything runs.

TransformUniqueWhat it does
addressnoReplaces a street address with a synthetic one of a similar shape.
citynoReplaces a city with a synthetic one.
companynoReplaces a company name with a synthetic one that reads as a company.
credit_cardnoReplaces a card number with a Luhn valid test number, so a payment form still validates it and no real card is ever present.
date_shiftnoMoves a date or timestamp by a deterministic offset of up to a year, keeping its format and its time of day.
emailyesReplaces an address with a unique synthetic one at example.test, which is reserved and can never receive mail.
first_namenoReplaces a given name with a synthetic one.
free_textnoReplaces prose with synthetic prose of a similar length, so a layout built for three paragraphs still gets three paragraphs.
hash_hexyesReplaces a value with a keyed hash of the same length. Equality is preserved and nothing else is.
int_fpenoReplaces an integer with a different one of the same digit count and sign, so range checks and column widths still hold.
ipnoReplaces an IP address with one from a documentation range reserved by RFC 5737, which can never route anywhere.
last_namenoReplaces a family name with a synthetic one.
namenoReplaces a person’s name with a synthetic one of a similar shape, keeping the number of parts.
nullifynoSets the column to null. This is the default for unclassified free text, because a column nobody has confirmed is safe is not safe.
numeric_noisenoMoves a number by up to ten percent, keeping its sign, scale, and decimal places, so totals stay the right order of magnitude.
phonenoReplaces the digits of a phone number in place, keeping its length, punctuation, and country prefix so that format checks still pass.
postcodenoRewrites a postal code in place, keeping letters as letters and digits as digits so the country’s format still validates.
preserveyesLeaves the value unchanged. Use it to record that a column was reviewed and found safe, rather than leaving it out.
string_fpenoReplaces a string with one of the same length, keeping digits as digits and letters as letters so a format check still matches.
urlnoKeeps a URL’s scheme and path shape, replacing its host with a synthetic one at example.test.
usernameyesReplaces a handle with a unique synthetic one made of a word and a number.
uuid_remapyesMaps a UUID to a different valid UUID. Columns that share a link map identically, so foreign keys still join.

A transform has to satisfy the constraints the column already has.

AF-MSK-004 Masking would violate the check constraint orders_total_positive on
orders.total.
Next: Choose a format preserving transform for orders.total that satisfies
orders_total_positive.

numeric_noise keeps a number’s sign and scale and will satisfy most range checks. int_fpe keeps the digit count and sign. A check constraint that encodes a business rule, such as a status being one of five strings, needs preserve rather than a transform: there is no synthetic value that satisfies it and is not the original.

The question is what a test depends on.

A form that validates a card number needs credit_card, which produces a Luhn valid test number. A layout built for three paragraphs needs free_text, which produces three paragraphs. A report that sums a column needs numeric_noise, which keeps totals the right order of magnitude, rather than int_fpe, which does not.

A column that nothing reads can have nullify, and that is the default for unclassified free text on purpose: it makes the absence visible.

AF-MSK-007 The transform on users.email produced duplicate values under the
unique constraint users_email_key.
Next: Use a transform that preserves uniqueness, such as email or uuid_remap,
for users.email.

email, uuid_remap, username, hash_hex, int_fpe and string_fpe preserve uniqueness. name, city, company and the rest do not, because two people can share a name and pretending otherwise would mean generating increasingly unlikely ones to satisfy a constraint the data never had.

Every transform is keyed. The same input maps to the same output within one golden, which is what makes link work and what makes a masked database self-consistent. Across goldens the key differs, so the mapping cannot be reversed by diffing two refreshes.

Related: masking, verification.