Skip to content
Talk to our solutions team

Field compliance directives

A compliance directive declares how a field must be handled: at rest, on read, in logs, in exports and in indexes. It is a data-classification and protection control on your schema.

fields:
- name: national_id
type: string
compliances: [pii]

That single tag is not a label. It applies a protection bundle.

Tagging a field with a classification, pii, phi, pci or gdpr, automatically applies:

DimensionDefaultWhy
At restencryptedCiphertext in the column
On readmaskOutput shows a masked representation
Logs and copiesnologThe value never enters a log line, an audit row or an event payload
ExportsnoexportNever appears in a schema or data export
IndexesnoindexOnly when encrypted or tokenized. A plaintext index over ciphertext would defeat the encryption

The reasoning is worth stating plainly: a schema author should not be able to leave regulated data unprotected by forgetting a directive. Classification is the thing people remember; the bundle is what they forget.

Each dimension is applied only if you have not already expressed a conflicting directive for that dimension. The dimensions are independent, so overriding one leaves the rest in place.

DimensionDirectives (mutually exclusive)
On readmask · redact · hidden
At restencrypted · tokenize · hash
compliances: [pii, redact] # redacted on read, still encrypted, nolog, noexport
compliances: [pii, tokenize] # tokenized at rest, still masked on read

nolog and noexport are additive. They never conflict with anything, so for a classified field they always apply. You cannot accidentally turn them off by adding another directive.

DirectiveEffect
maskOutput shows a masked representation
redactOutput shows a redaction marker
hiddenThe field is omitted from output entirely
DirectiveEffect
encryptedCiphertext in the column
tokenizeStored as a token; the value lives elsewhere
hashOne-way. The original is not recoverable

An audit row (history: { strategy: typeaudit }) and an event payload (events:, the outbox, webhooks) are copies of a row’s values. A read is shaped for the principal reading it; a copy has no principal, so it takes the most restrictive shape the field allows:

DirectiveIn <entity>_auditIn an event payload
hiddenNo row for the fieldField absent
nolog, redact[REDACTED] on both sides; the row still says the field changed[REDACTED]
maskThe masked valueThe masked value
encrypted, tokenize, hashThe stored form (ciphertext, token, hash)The stored form

Whether a field changed is decided on the real values before the copy is shaped, so two values that mask alike still produce an audit row. An embeddable’s sub-fields follow the same rules inside the document.

DirectiveEffect
nologNever enters a log line, an audit row or an event payload
noexportNever appears in a schema or data export
noindexNo index is emitted over the column
DirectiveMeans
piiPersonally identifiable information
phiProtected health information
pciPayment card data
gdprIn scope for GDPR
DirectiveEffect
searchableSee below
retentionMarks the field for the retention runner. Parsed here, enforced there

Encryption and querying pull in opposite directions, an encrypted email column cannot be looked up by value.

searchable is the escape hatch: it suppresses default encryption for a classified field that must remain queryable.

- name: email
type: string
compliances: [pii, searchable]

Mask, nolog and noexport still apply. The value is still masked on read, still kept out of logs and exports, only the at-rest encryption is dropped, and only because you asked.

Everything that needs a field’s posture goes through the same resolution: read masking, at-rest encryption on both write and read, the schema exporters, and the index emitter during migration.

That matters because it is the only way the policy stays coherent. If masking resolved compliances differently from export, a field could be masked in an API response and plain in a CSV, which is exactly the failure this design exists to prevent.

Default encryption is a no-op at runtime when no key provider is configured: the encrypting hook skips.

That keeps the policy from breaking a deployment that has not set up keys yet. It also means a classified field can be sitting in plaintext while the schema says encrypted, so verify key configuration before treating classification as protection.

They are easy to confuse and do different jobs.

ComplianceTransform
GovernsWho may see the valueWhat the value is
AppliedAt read, write, export and index, per policyOn write, in declared order
hashProtection policy. A bundle dimensionA function that rewrites the stored value
Reversibleencrypted and tokenize areOnly if the function is

A masked field still stores the real value. A mask_partial transform changes what is stored. Reach for the compliance when the requirement is about access, and the transform when it is about content.